Assign a for loop and it collects — the last expression of each
iteration becomes an element of the result:
1 2 3
squares = for x in [1, 2, 3, 4, 5]
x * x
log[squares]expected output — from the book, verified by crow test --book
[1, 4, 9, 16, 25]A trailing if filters — iterations that produce no value contribute
nothing:
1 2 3 4
evens = for x in range[10]
if x % 2 eq 0
x
log[evens]expected output — from the book, verified by crow test --book
[0, 2, 4, 6, 8]This one construct is Crow's map, filter, and list comprehension —
none of which exist separately, because they'd all be second ways to
write this.
Those iterations did not run in order. Crow analyzes what each statement
reads and writes and runs independent work concurrently — a for over ten
thousand rows uses every core, for free:
1 2 3 4
celsius = [0.0, 21.5, 100.0]
fahrenheit = for c in celsius
c * 9.0 / 5.0 + 32.0
log[fahrenheit]expected output — from the book, verified by crow test --book
[32, 70.7, 212]The results arrive in input order — collection is deterministic. The
iterations run in parallel only when nothing in the body can show the
order they ran in. A body that prints, writes a file or a fact, draws a
random number, renders a view, or accumulates into an outer variable
(total = total + n) runs in order, so it stays correct and just
forfeits the parallelism. A function you call counts by what it does,
however deep: a loop over a function that only computes runs on every
core, one over a function that logs runs in order. crow build --ir
shows which loops run in parallel. The idiom that keeps a loop parallel
is the one you've been seeing all book: compute in the loop, collect
the values, print after.
cpu[8] before a loop caps its thread count; cpu[1] runs every loop on
one core.