Rayforce ← Back to home
GitHub

Control Flow & Error Handling

Conditionals, variable binding, lambdas, error handling, null semantics, and higher-order functions in Rayfall.

Conditionals: if

The if special form evaluates a condition and returns the corresponding branch:

 (if (> 5 0) "positive" "non-positive")
"positive"

Without an else branch, if returns 0:

 (if 0 "yes")
0

Sequential Evaluation: do

do evaluates expressions in order and returns the last result. let bindings inside do are scoped to that block:

 (do
    (set x 10)
    (set y 20)
    (+ x y))
30

Variable Binding: set and let

set creates a global binding. let creates a local binding scoped to the enclosing do:

 (set x 42)
42

 (do (let y 10) y)
10

The variable y is not visible outside the do block.

Lambda Functions: fn

Create anonymous functions with fn. Parameters are listed in square brackets:

 (set add1 (fn [x] (+ x 1)))
 (add1 5)
6

Recursive lambdas use self to refer to the enclosing function:

 (set fib (fn [n] (if (<= n 1) n (+ (self (- n 1)) (self (- n 2))))))
 (fib 10)
55

Error Handling: try / raise

raise throws an error with an arbitrary value. try catches it and passes the value to a handler function:

 (try (raise 42) (fn [e] e))
42

 (try (raise 42) (fn [e] (+ e 1)))
43

 (try (raise "boom") (fn [e] "caught"))
"caught"

If no error is raised, try returns the result of the body expression normally. Works inside lambdas compiled to bytecode.

Early Return: return

return exits a lambda early with the given value:

 (set f (fn [x] (if (< x 0) (return -1) (+ x 1))))
 (f -5)
-1
 (f 5)
6

Null Semantics

Nulls are tracked via a per-element bitmap, not sentinel values. Typed null literals create atoms with the null bit set:

Literal Type
0Nli64 null
0Nff64 null
0Nii32 null
0Nddate null
0Nttime null
0Nptimestamp null
0Nssymbol null

Null rules:

 (nil? 0Nl)
true

 (+ 0Nl 1)
0Nl

 (nil? (println "hello"))
hello
true

Higher-Order Functions

Lambdas are auto-mapped over vectors when called directly. Use map for explicit element-wise application, fold for reductions, and scan for running accumulations:

;; map applies a function to each element
 (map (fn [x] (* x x)) [1 2 3 4 5])
[1 4 9 16 25]

;; lambdas auto-map over vectors
 ((fn [x] (* x x)) (til 5))
[0 1 4 9 16]

;; fold reduces a vector with a binary function
 (fold + 0 (til 10))
45

;; scan produces running accumulations
 (scan + [1 2 3 4 5])
[1 3 6 10 15]

;; where returns indices matching a condition
 (where (> (til 10) 3))
[4 5 6 7 8 9]