Reactive bindings and contracts bind creates a variable that recomputes automatically when its dependencies change. where clauses add runtime enforcement to bindings and parameters.
bind total = price * qty tracks which variables are read when the expression is first evaluated. When any of those variables are reassigned, the binding recomputes.
var price = 10
var qty = 3
bind total = price * qty
println ( total ) -- 30
price = 20
println ( total ) -- 60
qty = 5
println ( total ) -- 100
Works with strings and any expression:
var name = " world "
bind greeting = " hello " ++ name
println ( greeting ) -- hello world
name = " xs "
println ( greeting ) -- hello xs
A bind can depend on another bind. When the root dependency changes, all downstream bindings update in order.
var price = 10
var qty = 2
bind total = price * qty
bind doubled = total * 2
println ( total ) -- 20
println ( doubled ) -- 40
price = 5
println ( total ) -- 10
println ( doubled ) -- 20
Reactivity is implemented in the bytecode VM and JIT by replaying the bound expression on dependency change. The transpilers (--emit js, --emit c, --emit wasm) lower bind as a regular let since static targets cannot observe variable mutation through the same hook. Add a where clause after a type annotation to enforce a condition at runtime. The condition is checked when the binding is evaluated or the parameter is passed.
let age : int where age > 0 and age < 150 = 25
println ( age ) -- 25
try {
let bad : int where bad > 100 = 5
} catch e {
println ( e ) -- contract violation
}
Contracts on function parameters enforce preconditions at call sites:
fn divide ( a : int , b : int where b != 0 ) {
return a / b
}
println ( divide ( 10 , 2 ) ) -- 5
try {
divide ( 10 , 0 )
} catch e {
println ( e ) -- contract violation
}
Contracts are gradual: omitting a where clause means no checking. Add them where the invariant matters and the cost of checking is justified.