Mutable locals & loops

Implemented

var, assignment, while, for x in xs, break, continue and return: ordinary idioms that desugar before hashing.

let is immutable. var declares a mutable local, and x = e assigns to it. Loops are while and for x in xs. All of it is sugar: it is rewritten into recursive helper functions before hashing, so the stored definition (and its hash) is the same as for the hand-written recursion. A while loop and the equivalent recursion deduplicate to one object.

let collatz = (n: int): int => {
var cur = n
var steps = 0
while (cur != 1) {
  cur = if cur % 2 == 0 { cur / 2 } else { 3 * cur + 1 }
  steps = steps + 1
}
steps
}

let sum = (xs: list<int>): int => {
var total = 0
for x in xs {
  total = total + x
}
total
}
Verified: collatz(6) is 8 and sum([1, 2, 3]) is 6, both through UCM.

The condition of a while needs its parentheses.

Leaving early

return e leaves the nearest function, break leaves and continue skips to the next iteration of the nearest loop. A guard if c { ...; return e } hashes like the equivalent if/else.

let firstBig = (xs: list<int>): int => {
for x in xs {
  if x > 10 { return x }
}
0
}
Verified: firstBig([1, 20, 30]) is 20.

return is allowed as a statement of a function body (through guards and loops); it is refused inside a switch arm, a handler clause or a let value. A lambda inside a loop is a different function, so a jump there is an error.

To walk a dictionary, destructure each entry: for (k, v) in d { ... } visits the entries in sorted key order.

let total = (d: dict<int>): int => {
var sum = 0
for (k, v) in d {
  sum = sum + v
}
sum
}
Verified through clay add and eval: summing the dict with entries a=1 and b=2 gives 3.

The one hard rule

No lambda or local function may mention a var, at all. This is a purely syntactic check, and it is what keeps every closure shippable between machines without its behaviour depending on shared mutable state. State that has to live across calls goes through an explicit State ability (see Abilities).

Not built

Assignment through a field path (a.b.c = 1, see Records) and a dict entry assignment (d["k"] = v, a.tags["k"] = v) are built; list-element assignment (xs[i] = v) is not. In a loop body the path form needs the record type to be known, so use a helper function there.