What you will build
First, a billing toolkit whose functions work across scalar values, open records, and collections. Then, a closed order lifecycle whose possible states and payloads are checked by the compiler.
1. Read the function boundary
Open the starter above or the canonical Named Function Toolkit:
identity(value) = value
net_of_fee(amount, fee_rate) = amount * (1 - fee_rate)
rush_premium(job) = job.amount * job.rush_rate
floor_at(value, minimum) = MAX(value, minimum)
same_shape(values) = MAP(values, value => value)
billed_total(jobs) = REDUCE(0, jobs, (acc, job) => acc + job.amount)
Grid infers one generalized signature for each name. identity can accept unrelated value kinds at different call sites; rush_premium requires the fields it reads while allowing extra record fields.
Checkpoint:
B1 = 42,B2 = "invoice-run",C1 = 4,576,C2 = 740, andC3 = 0.
2. Reuse functions over rows and collections
D1 = MAP(Jobs, job => net_of_fee(job.amount, A1))
D2 = MAP(Jobs, job => rush_premium(job))
D3 IS currency = billed_total(Jobs)
D4 IS currency = REDUCE(0, D2, (acc, value) => acc + value)
D5 = MAP(D1, value => floor_at(value - A2, 0))
Checkpoint:
D1 = [4576, 2728, 6512],D2 = [1300, 0, 740],D3 = 15,700,D4 = 2,040, andD5 = [4176, 2328, 6112].
same_shape also preserves both a simple array and the Jobs collection:
H1 = same_shape([10, 20, 30])
H2 = same_shape(Jobs)
Checkpoint:
H1 = [10, 20, 30];H2retains the three original job records and their extra fields.
3. Verify the composed result
L1 IS currency = D3 * (1 - A1) + D4
L2 = L1 > 12000 THEN "healthy-month" ELSE "lean-month"
L3 = TEXT(L1, "0.00")
L4 = `net={L3} status={L2}`
Checkpoint:
L1 = 15,856,L2 = "healthy-month",L3 = "15856.00", andL4 = "net=15856.00 status=healthy-month".
4. Declare a closed lifecycle
Open the canonical Algebraic Choice Lifecycle:
CHOICE Order =
Draft |
Placed(total: number) |
Shipped(total: number, transit_days: number) |
Cancelled(reason: string)
Each value is exactly one constructor with a checked payload:
A1 = Order.Placed(1840)
A2 = Order.Shipped(2620, 3)
A3 = Order.Cancelled("payment declined")
A4 = Order.Draft()
5. Match every state
B1 = MATCH(
A2,
Order.Draft() -> "draft",
Order.Placed(total) -> "placed " & TEXT(total, "0"),
Order.Shipped(total, transit_days) -> "shipped " & TEXT(transit_days, "0") & "d",
Order.Cancelled(reason) -> "cancelled: " & reason
)
Checkpoint:
B1 = "shipped 3d",B2 = "priority-pick", andB3 = 2,620.
A guarded Placed arm does not make the match exhaustive; its unguarded companion handles every other placed order. Temporarily remove Order.Placed(_) -> "standard-pick" from B2.
Checkpoint: compilation reports a non-exhaustive MATCH and identifies the missing unguarded Placed case. Restore the arm.
6. Keep finite recursive state as data
The Approval choice stores a finite escalation chain. The nested matches fold it into a final approver and hop count.
Checkpoint:
D1 = "finance-chief",D2 = 2, andE1 = "final_approver=finance-chief escalation_hops=2 pick=priority-pick".
Try it yourself
In Named Function Toolkit, add:
priority_jobs(jobs, threshold) = FILTER(jobs, job => job.amount > threshold)
M1 IS currency = billed_total(priority_jobs(Jobs, 5000))
Checkpoint:
M1 = 12,600because Meridian and Harbor exceed the threshold.
In Algebraic Choice Lifecycle, change A1 to Order.Placed(1200).
Checkpoint:
B2 = "standard-pick"andE1ends withpick=standard-pick.
You are done when
- One named function is reused without duplicating its formula.
- A function accepts records with required and additional fields.
- Every Order constructor has an unguarded match path.
- Changing a constructor payload updates the dependent classification and summary.