Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Iterators

foreach can read an array, an Iterator<K, V>, or a ToIterator<K, V>.

$letters = dict['a' => 1, 'b' => 2];
foreach ($letters as $letter => $position) {
  write_line!($letter . ': ' . $position);
}

The key is optional:

foreach (vec['Ada', 'Grace'] as $name) {
  write_line!($name);
}

Iterator

An iterator owns one position. Its next() method returns the next key and value, or null after the last item.

interface Iterator<out K, out V> {
  public function next(): null|(K, V);
}

Passing an iterator to a second foreach does not rewind it. The second loop starts at the iterator’s current position.

ToIterator

A value that can start a new pass implements ToIterator<K, V>:

interface ToIterator<out K, out V> {
  public function toIterator(): Iterator<K, V>;
}

foreach calls toIterator() at the start of each pass. The method should return a new iterator unless the type has a clear reason to share a position.

Writing an iterable value

ArrayIterator turns any Whim array into an iterator.

use Whim\Iterate\ArrayIterator;
use Whim\Iterate\Iterator;
use Whim\Iterate\ToIterator;

final class Names implements ToIterator<int, string> {
  public function __construct(private vec<string> $names) {}

  public function toIterator(): Iterator<int, string> {
    return new ArrayIterator::<int, string>($this->names);
  }
}

$names = new Names(vec['Ada', 'Grace']);
foreach ($names as $index => $name) {
  write_line!($index . ': ' . $name);
}

Iterable types

Whim\Refine\Iterable<K, V> is the union of:

  • Iterator<K, V>
  • ToIterator<K, V>
  • array<K, V>

Library functions use this type when they only need to read a sequence. Such a function accepts a tuple, vec, dict, iterator, or iterable object without first copying it.

An iterator may yield data once. Do not assume that an Iterable can start a second pass unless it is an array or implements ToIterator.

Iterator helpers

map, filter, take, drop, and join return lazy iterators. They read an entry only when their next() method needs one. map and filter keep the source keys. take does not read past its limit.

Their concrete types are public: MapIterator, FilterIterator, TakeIterator, DropIterator, and JoinIterator. Use the helper functions for short pipelines, or construct these types when an API needs a concrete adapter.

count, reduce, to_vec, and to_dict consume the rest of an iterable. to_dict keeps its keys; to_vec keeps only its values.

use Whim\Iterate;

$values = Iterate\take::<int, int>(
  Iterate\filter::<int, int>(
    Iterate\map::<int, int, int>(
      vec[1, 2, 3, 4],
      fn(int $value): int => $value * 2,
    ),
    fn(int $value): bool => $value > 4,
  ),
  1,
);

assert!(Iterate\to_vec::<int, int>($values) == vec[6]);

Functions in Whim\Vec and Whim\Dict return complete arrays instead.

Use a lazy iterator for a long stream or when the caller may stop early. Use a vec or dict when the caller needs random access, a count, or more than one pass.