Thunks & Laziness
Milang is eager by default — every expression is evaluated as soon as it is
bound. The tilde operator ~ lets you opt into delayed evaluation where you
need it.
Creating thunks with ~
Prefixing an expression with ~ wraps it in a thunk: a suspended computation
that is not executed until its value is actually needed.
eager = 1 + 2 -- evaluated immediately
delayed = ~(1 + 2) -- wrapped in a thunk; not yet evaluated
result = delayed -- forced here: evaluates to 3
build = {target = c, os = linux, arch = x86_64}
eager = 3
delayed = 3
result = 3
When a thunk is used in a context that needs its value (passed to an operator, printed, pattern-matched, etc.) it is forced automatically — you never call a thunk explicitly.
if and auto-quote parameters
In earlier versions of milang, if required explicit thunks on both branches
to prevent eager evaluation:
-- Old style (still works, but no longer necessary):
result = if (x > 5) (x * 2) (x * 3)
The if conditional quotes its branches implicitly; write conditionals like this:
x = 10
result = if (x > 5) (x * 2) (x * 3)
build = {target = c, os = linux, arch = x86_64}
x = 10
result = 20
Both styles work — if you pass a thunk to an auto-quote parameter, the thunk
is forced after splicing. See the Metaprogramming chapter
for details on #-params.
Nested conditionals
Conditionals compose naturally:
z = 7
result = if (z > 10) 100 (if (z > 5) 50 0)
build = {target = c, os = linux, arch = x86_64}
z = 7
result = 50
Each inner if is only evaluated when its enclosing branch is selected.
Lazy bindings with :=
The := operator creates a lazy binding — syntactic sugar for a thunk that
caches its result after the first force:
x = 3
y := x + 10 -- not evaluated until y is used
z = y * 2 -- forces y here; y becomes 13, z becomes 26
build = {target = c, os = linux, arch = x86_64}
x = 3
y = <closure>
z = 26
Lazy bindings are useful for expensive computations that may never be needed, or for establishing declaration-order dependencies without paying upfront cost.
When to use thunks
| Situation | Mechanism |
|---|---|
Conditional branches (if) | Auto-quoted branch parameters handle this |
Short-circuit logic (&&, ||) | Auto-quote params handle the lazy operand |
| Deferred expensive work | Lazy binding := |
| Controlling IO ordering | Thunks delay side effects until forced |
The general rule: reach for ~ whenever you need to control when an
expression is evaluated rather than relying on milang’s default left-to-right
eager order.