Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

SituationMechanism
Conditional branches (if)Auto-quoted branch parameters handle this
Short-circuit logic (&&, ||)Auto-quote params handle the lazy operand
Deferred expensive workLazy binding :=
Controlling IO orderingThunks 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.