Course of Raku / Functional, concurrent, reactive, and web programming / Functional programming / Iterators
The iterator protocol
Every value you can loop over — a list, a range, an array — can hand
you an iterator with the .iterator method. An
iterator has one essential method, pull-one, which returns
the next value each time you call it:
my $it = (10, 20, 30).iterator;
say $it.pull-one; # 10
say $it.pull-one; # 20
say $it.pull-one; # 30When the values run out, pull-one returns a special
sentinel, IterationEnd, instead of a real element. Asking
the same $it for one more value shows it:
say $it.pull-one =:= IterationEnd; # TrueThe =:= operator tests for the same
object, which is how you recognise the sentinel. That detail
matters inside a loop, where you need to keep the pulled value
and test it. If you assign it with
=, the value lands in a container and the comparison ends
up looking at the container rather than at IterationEnd.
The remedy is to bind with :=, so the
variable simply is whatever pull-one returned:
my $it = <a b c>.iterator;
loop {
my $v := $it.pull-one; # bind, not assign
last if $v =:= IterationEnd;
say $v;
}The loop prints a, b, c and
then stops. This is exactly what a for loop does for you
under the hood: it calls .iterator on the thing you are
looping over and keeps calling pull-one until it meets
IterationEnd. You rarely need to write it out by hand, but
doing so once shows that iterators — not lists — are what Raku actually
loops over.
Practice
Complete the quiz that covers the contents of this topic.
Course navigation
← Iterators | Quiz — The iterator protocol →
💪 Or jump directly to the exercise
to this section.