"For" and "with" expressions

"For" loops

The for syntax is designed for iterating over collections.

main void() xs nat[] = 1, 2, 3 for x : xs info log "{x}"

It can be void as above, or it can be used to map over a collection.

main void() xs nat[] = 1, 2, 3 ys nat[] = for x : xs x * 2 for y : ys info log "{y}"

It can even produce a different type of collection.

main void() xs nat[] = 1, 2, 3 ys nat[string] = for x : xs "{x}", x * 2 info log "{ys["2"]!}"

It can be written on one line by using a ;.

main void() xs nat[] = 1, 2, 3 ys nat[] = for x : xs; x + 1 info log "{ys.to::json}"

Desugaring for loops

for is actually just syntax for calling a function for-loop and passing it a lambda.
(That is usually implemented using a loop or by delegating to another for-loop function.)

main void() "a" repeated 3 for-loop x => info log x for x : "b" repeated 2 info log x repeated record(value string, times nat) nominal for-loop void(a repeated, f void mut(x string)) for _ : 0::nat .. a.times f[a.value]

"For-break" loops

Sometimes you want to stop the loop early. For that, use break and continue like in a loop.

main void() xs nat[] = 1, 2, 3 for x : xs info log "{x}" if x is-even break

This desugars to calling a function for-break nat(xs nat[], f-body nat break-or-continue mut(x nat), f-else nat mut()).
The break and continue keywords desugar into constructing break-or-continue values.
That type is declared in misc.

Like in a loop, break can take a value.

main void() xs nat[] = 1, 2, 3 result nat? = for x : xs if x is-even break x info log "{result!}"

Since the break isn't guaranteed to be reached (since the collection could run out first), the result is wrapped in an option.
You could avoid that by providing an else block to provide a value if break isn't reached.

main void() xs nat[] = 1, 2, 3 result nat = for x : xs if x > 5 break x else 10 info log "{result}"

The above calls a function that works like for-break nat(xs nat[], f-body nat break-or-continue mut(x nat), f-else nat mut()).

"With" expressions

A related expression is with. Technically, this behaves exactly like for except that the function it calls is named with-block instead of for-loop.

However, using with instead of for implies that the body will usually execute only once.
It's not meant for loops.

One example use is to build a collection. build returns a type called build-options which is used as the argument to with-block.
The signature works like with-block string(a build-options, f void mut(out string-builder)).

main void() x string = with out : build out ~~= "crow" out ~~= "bar" info log x