comprehension
An array whose elements are computed. The clauses describe an iteration and expr runs once per
step. An expr of type T gives T[].
const bearings_deg: int32[] = [10, 45, 90];
const reciprocals_deg: int32[] = [b + 180 for b in bearings_deg];Clauses
A comprehension is the element expression followed by a flat list of clauses. The for keyword after
the first element is what distinguishes it from a plain array literal.
- The clauses are
for,if,let,scan, andzip for. - The list is freely ordered and each clause may repeat. There is no required
for … if … scanshape. - Clauses are processed left to right. Every binding is visible to the clauses after it and to the element expression, and not to itself.
- Each pattern is the same pattern a
lettakes: a name, a name with a type, a tuple to destructure, or_.
const bearings_deg: int32[] = [10, 45, 90];
[b + 1 for b in bearings_deg] // => [11, 46, 91]
[b for b in bearings_deg if b > 20] // => [45, 90]
[d for b in bearings_deg let d = b * 2] // => [20, 90, 180]
[s for b in bearings_deg scan s: int32 = 0 in s + b] // => [10, 55, 145]Flex has no for or while in an expression: a comprehension and fold, map, filter, and
flatmap are the whole of iteration, and loop belongs to a protocol. Every one of them produces a
value, so nothing iterates for its effects.
for
for pattern in source iterates the source. A second for is a nested loop, not a lockstep one: the
clauses form a cartesian product, and the leftmost for varies slowest.
A later source may name a binding an earlier clause introduced.
const sectors: int32[] = [1, 2];
const sweeps: int32[] = [10, 20];
[s + w for s in sectors for w in sweeps] // => [11, 21, 12, 22]if
if predicate drops the iterations where the predicate is false. The predicate has type bit.
let
let pattern = expr binds a value once per iteration, for the clauses after it and the element
expression. It is the place to name a subexpression that would otherwise be repeated.
scan
scan pattern = init in fold carries an accumulator across iterations. The type annotation is
optional and is inferred from the initializer when it is left out.
- The accumulator is visible in its own fold expression, in every later clause, and in the element expression. It is not visible in its own initializer.
- The element expression sees the accumulator for the current iteration — the value the fold expression just produced, not the one it started from.
- Several
scanclauses may be threaded through one comprehension.
const legs_m: int32[] = [100, 250, 400];
[total for leg in legs_m scan total: int32 = 0 in total + leg] // => [100, 350, 750]
[total for leg in legs_m scan total = (0: int32) in total + leg] // => [100, 350, 750]zip for
zip for pattern in source advances its source in lockstep with the frames already established
instead of crossing with them. The result is as long as the shortest source.
zipcannot appear withscanin one comprehension.zipcannot appear withifin one comprehension.
const sectors: int32[] = [1, 2, 3];
const sweeps: int32[] = [10, 20];
[s + w for s in sectors zip for w in sweeps] // => [11, 22]
[(s, w) for s in sectors zip for w in sweeps] // => [(1, 10), (2, 20)]Errors
A filter that is not bit
const bearings_deg: int32[] = [10, 45, 90];
const wrong: int32[] = [b for b in bearings_deg if b]; // error: comprehension-filter-typezip together with scan
const sectors: int32[] = [1, 2, 3];
const sweeps: int32[] = [10, 20];
const wrong: int32[] =
[t for s in sectors zip for w in sweeps scan t: int32 = 0 in t + s]; // error: comprehension-zip-scan-exclusivezip together with a filter
const sectors: int32[] = [1, 2, 3];
const sweeps: int32[] = [10, 20];
const wrong: int32[] = [s + w for s in sectors zip for w in sweeps if s > 1]; // error: comprehension-zip-filter-exclusiveRelated
- array — the plain literal, and the type a comprehension produces.
- fold, map, filter, flatmap — the same iteration as expressions with block bodies.
- block, let, assert — the patterns a clause binds with.