fold, map, filter, flatmap
Looping expressions over a collection. Each takes a source, binds one element at a time, and runs a block body whose tail expression is that iteration's result.
function total_range_m(ranges_m: int32[]) -> int32 = fold(total: int32 = 0; r in ranges_m) {
total + r;
};Sources and bindings
fold, map, filter, and flatmap are all expressions rather than statements.
foldtakes an array.map,filter, andflatmaptake an array or anOptional.- Over an
Optional, the body runs only when the source issome, and the result is anOptional. - The iteration variable is bound in the body and nowhere else.
- The variable takes the same patterns a
letdoes, so a tuple element can be destructured in the header. - Ascribing the variable a type pins the source's element type, which is how a collection of otherwise unpinned literals becomes usable.
fold
fold(acc = init; x in source) reduces an array left to right. Each body tail becomes the next
accumulator value, and the last one is the result.
- The accumulator's type annotation is optional, and is inferred from the initializer when omitted.
- The initializer must be assignable to the accumulator type, and so must the body tail.
- The result type is the accumulator type.
- A tuple accumulator carries several running values, and then the body must produce a tuple.
const legs_m: int32[] = [100, 250, 400];
fold(total: int32 = 0; leg in legs_m) { total + leg; } // => 750
fold(total = (0: int32); leg in legs_m) { total + leg; } // => 750
fold((lo, hi) = ((0: int32), (0: int32)); leg in legs_m) { (lo + leg, hi + (1: int32)); } // => (750, 3)map
map(x in source) applies the body to every contained value. An array of T becomes an array of the
body's type, and an Optional<T> becomes an Optional of it.
The body may have any type. It is the only one of these forms that changes the element type freely.
const bearings_deg: int32[] = [10, 45, 90];
map(b in bearings_deg) { b * 2; } // => [20, 90, 180]
map(b in some((45: int32))) { b * 2; } // => some(90)
map(b in (none: Optional<int32>)) { b * 2; } // => nonefilter
filter(x in source) keeps the values whose body is true. The result has the source's own type.
The body must have type bit. Filtering an Optional either keeps the some or turns it into none.
const bearings_deg: int32[] = [10, 45, 90];
filter(b in bearings_deg) { b > 20; } // => [45, 90]
filter(b in some((10: int32))) { b > 20; } // => noneflatmap
flatmap(x in source) maps each value to a collection and flattens one level. Arrays are concatenated
in iteration order; a nested optional collapses to a single one.
- The body must produce the same kind of collection as the source. An array source needs an array body, and an optional source an optional body.
- The body's element type need not relate to the source's.
- An empty array body contributes nothing, which is how one pass both filters and maps.
const bearings_deg: int32[] = [10, -45, 90];
flatmap(b in bearings_deg) { [b, b]; } // => [10, 10, -45, -45, 90, 90]
flatmap(b in bearings_deg) { if b > 0 then [b] else []; } // => [10, 90]
flatmap(b in some((45: int32))) { some(b * 2); } // => some(90)Errors
A fold over something that is not an array
function total(range_m: int32) -> int32 = fold(total: int32 = 0; r in range_m) { // error: fold-source-type
total + r;
};A fold initializer of the wrong type
function total(ranges_m: int32[]) -> int32 = fold(total: int32 = "none"; r in ranges_m) { // error: fold-init-type
total + r;
};A fold body that is not the accumulator's type
function total(ranges_m: int32[]) -> int32 = fold(total: int32 = 0; r in ranges_m) { // error: fold-body-type
"counted";
};A map over something that is neither an array nor an optional
function doubled(range_m: int32) -> int32[] = map(r in range_m) { // error: map-source-type
r * 2;
};A filter over something that is neither an array nor an optional
function nearby(range_m: int32) -> int32 = filter(r in range_m) { // error: filter-source-type
r < 500;
};A filter body that is not bit
function nearby(ranges_m: int32[]) -> int32[] = filter(r in ranges_m) { // error: filter-body-type
r * 2;
};A flatmap over something that is neither an array nor an optional
function paired(range_m: int32) -> int32[] = flatmap(r in range_m) { // error: flatmap-source-type
[r, r];
};A flatmap body that is not a collection
function paired(ranges_m: int32[]) -> int32[] = // error: type-mismatch
flatmap(r in ranges_m) { // error: flatmap-body-type
r;
};Related
- comprehension — the same iteration in bracket form, with clauses instead of a body.
- array — the source all of these are usually written over.
- optional — the other source, and what a
mapover one returns.