Skip to main content

Collections & Iteration

Lists

The workhorse collection is List, written with square brackets. Elements can be anything, and needn't share a type:

xs = [1, "foo", []]

Indexing is 1-based, with the usual bracket syntax:

xs[2] # "foo"
xs[2] = 10 # update (creates a new value, as ever)

The core operations:

length(xs)
append(&xs, 4) # add to the end
pop(&xs) # remove from the end, returning it
empty?(xs)

Remember that lists are values: append(&ys, 4) changes the variable ys and nothing else.

Ranges

range(a, b) is an iterable range, inclusive at both ends:

for i = range(1, 10) { println(i) }
collect(range(1, 5)) # [1, 2, 3, 4, 5]

map, collect, reduce

The functional staples are here:

fn twice(x) { x * 2 }

collect(map(range(1, 3), twice)) # [2, 4, 6]
reduce(range(1, 4), +) # 10
sum(xs)

map is lazy – it produces a Mapping you can iterate; collect realises any iterable into a list. Note there's no lambda syntax yet, so map takes named functions.

Sequences

The standard library also has Sequence, an immutable linked list in the Lisp tradition, built from prepend cells:

s = seq(1, 2, 3)
first(s) # 1
rest(s) # seq(2, 3)
prepend(s, 0)

Lists are better for indexing and appending; sequences for sharing structure and recursion.

The iteration protocol

for loops, map, collect and friends work on anything iterable. The protocol is two functions:

  • iterate(xs) returns an iterator value.
  • next(&itr) returns Some(value) and advances the iterator, or nil when exhausted.

Any type can join by extending them:

bundle Countdown(n)

@extend
fn iterate(c: Countdown) { c }

@extend
fn next(&c: Countdown(n)) {
if n == 0 { return nil }
c = Countdown(n - 1)
return Some(n)
}

collect(Countdown(3)) # [3, 2, 1]

For simple indexable collections, IndexIterator(xs) gives you iteration for free once length and indexing are defined.

What's missing

There are no hash maps or sets yet – it's the standard library's most obvious gap, and high on the roadmap. For small key/value needs, records can stand in.