Laziness for the impatient

masak on 2009-10-11T15:35:54

In the future — the not too distant future, if I read the ROADMAP correctly — Rakudo will be able to handle potentially infinite streams of values.

my @a = gather {
    my $count = 5;
    take $count++ while True;
};

.say for @a[0..4]; # 5\n6\n7\n8\n9

Actually, not only the gather construct does this, but lists themselves:

my @a = 5 ... *;
.say for @a[0..4]; # 5\n6\n7\n8\n9

Neither of these two work yet. The former hangs, and the latter just earned me a rakudobug. (Update 2009-10-13: A rakudobug which, 46 hours later, moritz++ fixes. Complaining is extra rewarding when the reaction is swift.)

Anyway, awaiting all that lazy goodness, we can always fall back on the "old-fashioned" way of lazily generating a stream, namely a closure. The following code does work in Rakudo:

class LazyIterator {
    has $!it;

    method get() {
        $!it();
    }
}

class Counter is LazyIterator {
    method new(Int $start) {
        my $count = $start;
        self.bless(*, :it({ $count++ }));
    }
}

my $c = Counter.new(5);
say $c.get for 0..4; # 5\n6\n7\n8\n9

In our daily fight against scattered and tangled code, closures are a fine weapon to have in one's arsenal. The fact that they are capable of giving us laziness in Rakudo today, before it is actually implemented, is a nice example of that.