Module /fusion/for
The for-family of syntax forms enables concise, consistent iteration
over many different varieties of aggregate data types, known as
series types.
The variants differ in how the results are accumulated, but all accept the same
inputs and traverse them in the same manner.
The basic form is for itself:
(for [(a [1, 2]),
(b (sexp "a" "b"))]
(displayln (sexp a b)))
Here, a and b are bound successively to the individual elements of a list
and a sexp, respectively, then the body (a call to display) is evaluated
with those bindings. Next, a and b are given fresh storage locations and
new bindings to the next elements of the series.
Since for always returns void, it's used to perform side effects; the example
above prints two lines:
(1 "a")
(2 "b")
For-clauses
Common to all for variants is the for-clause, a syntax fragment that
declares one or more identifiers and an expression evaluating to a series value.
Syntactically, these are similar to the binding clauses of the let-family of
binding forms, allowing one or more bound identifiers:
for-clause := (id series_expr)
| ((id ...+) series_expr)
The second form is used with series that produce multiple values:
(for_list [((name value) { "a": 1, "b": 2 })]
(display_to_string name "=" value))
=>
["a=1", "b=2"]
In all forms, the series_exprs are evaluated once before iteration begins,
and the result must be a value of one of the series types. The number of ids
in a for-clause must match the number of values produced by each step of the
series.
Series Types
A series is a value that can be traversed by the for forms. Traversing a
series consists of a sequence of steps, and each step produces one or more
values (consistent through the entire traversal).
Depending on the type of series, the order in which the series is traversed may
or may not be predictable.
For more information on the types that can be used as series, and other ways to create them, see /fusion/series.
Flat and Nested Traversal
The family variants come in pairs, starting with for or fors. When given a
single for-clause, these behave identically, but given multiple clauses they
behave differently.
A for expression traverses all of its series in lock-step, taking one step on
each series for each evaluation of the body:
(for [(a [1, 2]),
(b [5, 7])]
(display (sexp a b)))
=>
(1 5)(2 7)
A fors expression traverses its series as nested loops, as if each clause
were given in its own nested for expression:
(fors [(a [1, 2]),
(b [5, 7])]
(display (sexp a b)))
=>
(1 5)(1 7)(2 5)(2 7)
This is similar to the distinction between let and lets, and like those
forms the bound identifiers have different scopes in the two forms.
Exported Bindings
(for [for-clause, ...] body ...+)
for-clause := (id series_expr)
| ((id ...+) series_expr)
Iterates series in parallel, binding the corresponding ids to the values
produced at each step and then evaluating body.
The series_exprs are first evaluated left-to-right, and each must return
a series value. While every series can produce more elements, they are retrieved
in left-to-right order, bound to their ids at new storage slots, and then the
body is evaluated.
The results of the body are ignored; the result of for is always void.
If no for-clauses are provided, then the body is executed once.
(for [(a [1, 2]),
(b [5, 7])]
(display (sexp a b)))
=>
(1 5)(2 7)
(for_fold
[(accum-id init-expr), ...]
[for-clause, ...]
body ...+)
Iterates using explicit accumulators.
The first clause is used to initialize any accumulator bindings before iteration
begins; new accumulator locations are used for each cycle.
The final body expression must produce the same number of values as there are
accum-ids, and those become the accumulator values for the next cycle.
The results of the for_fold expression are the final accumulator values.
(for_fold
[(result "")]
[((name value) { "a": 1, "b": 2 })]
(string_append result
(if (== result "") "" " and ")
(display_to_string name "=" value))) => "a=1 and b=2"
(for_list [for-clause, ...] body ...+)
Like for, but returning a stretchy list of the body results.
(for_list
[(even [0, 2, 4]),
(odd [1, 3, 5])]
(+ even odd)) => [1, 5, 9]
(for_sexp [for-clause, ...] body ...+)
Like for, but returning a sexp of the body results.
(for_sexp
[(even [0, 2, 4]),
(odd [1, 3, 5])]
(+ even odd)) => (1 5 9)
(for_struct [for-clause, ...] body ...+)
Like for, but returning a struct. The body must return two values, a field
name (string or symbol) and its corresponding element.
(for_struct
[(key (quote (a b c))),
(val [1, 2, 3])]
(values key val)) => {a:1, b:2, c:3}
If the same field name is returned by multiple evaluations of the body, the resulting struct will have corresponding repeated fields:
(for_struct
[(key ["a", "b", "a"]),
(val [1, 2, 3])]
(values key val)) => {a:1, a:3, b:2}
(fors [for-clause, ...] body ...+)
Like for, but the series are iterated in nested loops.
The scope of each binding identifier includes the series_exprs of later
for-clauses, as well as the body.
(fors [(a [1, 2]),
(b [5, 7])]
(display (sexp a b)))
=>
(1 5)(1 7)(2 5)(2 7)
(fors_fold
[(accum-id init-expr), ...]
[for-clause, ...]
body ...+)
Like for_fold, except the series are iterated in nested loops instead of in
parallel. Each clause's bindings are made available to the following clauses
as well as the body.
(fors_list [for-clause, ...] body ...+)
Like fors, but returning a stretchy list of the body results.
(fors_list [(a [1,2]),
(b [5,7])]
(* a b)) => [5, 7, 10, 14]
(define data [{a:[ {b:1}, {b:2} ]},
{a:[ {b:3}, {b:4} ]} ])
(fors_list [(x data),
(y (. x "a"))]
(. y "b")) => [1, 2, 3, 4]
(define data [[[1],[2]],[[3],[4]]])
(fors_list [(x data),
(y x),
(z y)]
z) => [1, 2, 3, 4]
(fors_sexp [for-clause, ...] body ...+)
Like fors, but returning a sexp of the body results.
(fors_sexp [(a [1, 2]),
(b [5, 7])]
(* a b)) => (5 7 10 14)
(fors_struct [for-clause, ...] body ...+)
Like fors, but returning a struct. The body must return two values, a field
name (string or symbol) and its corresponding element.
As with for_struct, the result can have repeated fields:
(fors_struct [(v [1, 2]),
(k ["a", "b"])]
(values k v)) => {a:1, a:2, b:1, b:2}