A generator object for Perl 5

I have recently started a new job and it has forced me to learn more Python than I have ever had need to learn. I decided that I should take this as an opportunity to learn, and as Miyagawa-san has often done, steal when possible.

One thing that fascinated me is Python’s yield or generator pattern. In this pattern, you can make a function (or my case an object) which implements a lazy iterator returning a value (or possibly values (see below)) without leaving the while loop that generates them.

#!/usr/bin/env perl

use strict;
use warnings;

use Generator::Object;

my $evens = generator {
  my $i = 0;
  while (1) {
    $_->yield($i);
    $i += 2;
  }
};

use feature 'say';
say $evens->next; # 0
say $evens->next; # 2
say $evens->next; # 4

In the above example the generator body maintains an infinite while loop. It starts with the first call to next, and yields a value when the yield method is called, and at that point it waits for subsequent calls to next.

I have been having a hard time coming up with a usage that Perl doesn’t make possible given better closure support and state variables, but certainly the generator pattern makes for clean code in some cases. Its claim to fame is to help reduce the amount of information held in memory in certain algorithms or when reading data (though Perl already has mechanisms for this in many/most cases). Still I find it a nice pattern and one that I hope to use more.

As I have already shown, I have implemented a generator/yield pattern in Perl called Generator::Object. Under the hood it uses Coro as the pattern requires coroutines to support it. Indeed there were a couple modules that already did this, but their interface were not to my liking.

In my example you see my first major difference, the generator is an object and not a function. Further I localize the topic variable $_ to be the generator object itself when next is called on it. This makes it easy to access the methods on the object from within the generator. I have used this to make the generator more Perlish. For example, the object has a wantarray method which conveys the context in which next was called and thus the yield method may respond appropriately. Planned is the ability to pass arguments in to the generator and possibly even to the next method (think an aggregator type generator).

I hope you can enlighten me about cases in which the generator/yield pattern has marked differences, but until then, it still can be used to separate logic more cleanly if nothing else and I found it to be a fun toy project.

5 Comments

Feature request:

use overload '&{}' => sub {
  my $self = shift;
  sub { $self->next };
};

This would allow:

say $evens->();

… which is the most common way people do iterators in Perl.

To be truly idiomatic, you could overload <…>, though that may be of questionable taste.

Another vote for overloading ‘<>’

Leave a comment

About Joel Berger

user-pic As I delve into the deeper Perl magic I like to share what I can.