In role Iterator§

See primary documentation in context for method pull-one

method pull-one(Iterator:D: --> Mu)

This method stub ensures that classes implementing the Iterator role provide a method named pull-one.

The pull-one method is supposed to produce and return the next value if possible, or return the sentinel value IterationEnd if no more values could be produced.

my $i = (1 .. 3).iterator;
say $i.pull-one;       # OUTPUT: «1␤» 
say $i.pull-one;       # OUTPUT: «2␤» 
say $i.pull-one;       # OUTPUT: «3␤» 
say $i.pull-one.raku;  # OUTPUT: «IterationEnd␤»

As a more illustrative example of its use, here is a count down iterator along with a simplistic subroutine re-implementation of the for loop.

# works the same as (10 ... 1, 'lift off') 
class CountDown does Iterator {
    has Int:D $!current = 10;
 
    method pull-one ( --> Mu ) {
        my $result = $!current--;
        if $result ==  0 { return 'lift off' }
        if $result == -1 { return IterationEnd }
 
        # calling .pull-one again after it returns IterationEnd is undefined 
        if $result <= -2 {
            # so for fun we will give them nonsense data 
            return (1..10).pick;
        }
 
        return $result;
    }
}
 
sub forIterable:D $sequence&do --> Nil ) {
    my Iterator:D $iterator = $sequence.iterator;
 
    loop {
        # must bind the result so that =:= works 
        my Mu $pulled := $iterator.pull-one;
 
        # always check the result and make sure that .pull-one 
        # is not called again after it returns IterationEnd 
        if $pulled =:= IterationEnd { last }
 
        do$pulled );
    }
}
 
forSeq.new(CountDown.new), &say );  # OUTPUT: «10␤9␤8␤7␤6␤5␤4␤3␤2␤1␤lift off␤»

It would be more idiomatic to use while or until, and a sigilless variable.

until IterationEnd =:= (my \pulled = $iterator.pull-one{
    dopulled );
}