Iterating
for pulls from Iterator.next() through Iterate.iterate(), and the subject of a for is evaluated once into a temporary, so changing it inside the loop does not affect what is walked.
Iterator<Item> is a cursor with one member, next. Iterate<Item> is anything that can hand out such a cursor,
which is what for and every collection method (map, filter, fold, ...) are built on. A type takes part by
implementing Iterate itself.
Example#
type CountdownIterator with Iterator<Int> {
var current: Int
var fn next(): Int? {
if current <= 0 {
return None
}
current = current - 1
current + 1
}
}
type Countdown with Iterate<Int> {
start: Int
fn iterate(): Iterator<Int> {
CountdownIterator start
}
}
for value in Countdown(3) {
print value
}Syntax#
trait Iterator<Item> { var fn next(): Item? }
trait Iterate<Item> { fn iterate(): Iterator<Item> }
for <name> in <iterable> { ... }
for (<name>, <name>) in <iterable of tuples> { ... }Rules#
Iterator<Item>has exactly one member,next(): Item?, which answersNoneonce there is nothing left.Iterate<Item>has exactly one required member,iterate(): Iterator<Item>; every other method a collection has (map,filter,fold,toList(), ...) is a default method built from those two.for value in xs { ... }callsxs.iterate()once and thennext()until it answersNone. The loop variable is aconstcopy of each item, produced fresh by everynext(). To change the items themselves,for var item in xsbinds each slot of a container instead (Changing elements in place).The subject of a
foris evaluated once, into a temporary. Changing the binding named afterininside the loop body does not change what the loop walks, because the loop is already iterating a copy of the value the binding held when the loop started.var numbers = [1, 2, 3] for value in numbers { numbers.append(value * 10) } print numbersA
MapisIterate<(Key, Value)>, and aforhead can destructure the tuple directly.for (name, age) in ages { ... }binds both parts without a separate step.const ages: Map<String, Int> = ["Ada": 36, "Grace": 45] for (name, age) in ages { print "{name} is {age}" }A type takes part in
forand in every pipeline method by implementingIterate, with a privateIteratortype next to it that does the work.Countdownabove is the whole shape: one type that answers cursors, one type that is a cursor.
What this is not#
for is not a special form that works on every type - only on Iterate. A type with no iterate() cannot
stand after in.
for value in [1, 2, 3] {
print value
}type Point {
var x: Int
var y: Int
}
for value in Point(1, 2) {
print value
}
// error: `Point` is not `Iterate`, so `for` cannot walk itRelated#
- The collection traits - the five kinds, each built on top of
Iterate. - Pipelines - the lazy stages and terminal operations
Iterategives every collection for free. - Changing elements in place -
for var, whose variable is the slot and not a copy. - Bindings - why the loop variable is always a
const.