Iterators

Iterators

Iterators are the magic that makes foreach loops work.

The basic premise is that to iterate through something, you have to be able to know if there’s anything left, via hasNext?, and to retrieve the next element, via next.

As a bonus, for safe iteration, the remove method may be implemented. If it isn’t, though, it will return false.

Since iterators may iterate on all kinds of data structures, they are generic, ie. next will return a T.

Here’s a demonstration, iterating through characters of the word hellfire:

HellfireIterator: class extends Iterator<Char> {
    content := "hellfire"
    index := 0

    init: func

    hasNext?: func -> Bool {
      index < content size 
    }

    next: func -> Char {
      val := content[index]
      index += 1
      val
    }
}

For a foreach to work, one has to have an iterable type. Thankfully, an Iterator itself extends Iterable:

for (letter in HellfireIterator new()) {
  "%c" printfln(letter)
}

each and eachUntil

Apart from using foreach loops, one can use the each method:

HellfireIterator new() each(|letter|
  // do something with letter
)

Or the eachUntil, which will break if the passed closure returns false:

HellfireIterator new() eachUntil(|letter|
  if (letter == 'f') {
    return false // just hell, please 
  }

  // do something with letter
  true
)

reduce

An iterable can be reduced using the reduce method, accepting a closure. It’ll get called on each pair of two elements, until there is only one element left.

Example with a list of ints:

list := [1, 2, 3] as ArrayList<Int>
sum := list reduce(|a, b| a + b)

toList

Any Iterable<T> can be transformed to a List<T> via the toList method. Let’s try it on a string, which is iterable:

"ABC" toList() // gives ['A', 'B', 'C']