Skip to content

Generators

A generator is a function whose body contains yield. Calling it returns a generator object instead of executing the body. The body runs lazily, pausing at each yield and resuming on the next .next() call.

No special keyword is needed — any function or lambda that uses yield becomes a generator automatically.

func countdown(n) {
while (n > 0) { yield n; n = n - 1 }
}
let g = countdown(3)
print(g.next()) // {value: 3, done: false}
print(g.next()) // {value: 2, done: false}
print(g.next()) // {value: 1, done: false}
print(g.next()) // {value: nil, done: true}

.next() returns a map with value (the yielded value) and done (whether the generator is exhausted).

Generators work directly with for-in loops. Iteration is lazy — values are produced one at a time.

func range(n) {
for (i in 0..n) { yield i }
}
for (x in range(5)) { print(x) } // 0 1 2 3 4

Since generators are lazy, they can produce infinite sequences. Use break to stop.

func naturals() {
let n = 0
while (true) { yield n; n = n + 1 }
}
for (x in naturals()) {
if (x >= 5) { break }
print(x) // 0 1 2 3 4
}

yield is an expression that returns the value passed to .next(arg). The first .next() primes the generator; subsequent calls resume with the sent value.

func accumulator() {
let total = 0
while (true) {
let val = yield total
total = total + val
}
}
let acc = accumulator()
acc.next() // prime -- {value: 0, done: false}
acc.next(10) // {value: 10, done: false}
acc.next(5) // {value: 15, done: false}
acc.next(25) // {value: 40, done: false}

Lambdas can be generators too:

let squares = lam{ n in for (i in 0..n) { yield i * i } }
for (x in squares(5)) { print(x) } // 0 1 4 9 16

A return inside a generator sets done: true and the return value as the final value.

func gen() {
yield 1
return 99
}
let g = gen()
print(g.next()) // {value: 1, done: false}
print(g.next()) // {value: 99, done: true}
Property/MethodDescription
.next()Resume, return {value, done}
.next(val)Resume with sent value
.donetrue if generator is exhausted