How many of you have ever thought about how to implement coroutines in Perl?
Wow, I hadn't expected any. How many of you with your hands raised have thought about how you might avoid source filters and evil gotos?
Ok, with the exception of perhaps TheDamian, how many actually tried it? It seemed simple enough and I had a working solution in about 10 minutes:
- Break the code between the yields into code refs
- Create a dispatch table that knew about all the sections
- Create a tied variable that knows how to cycle through the dispatch table
- Return a closure calling the dispatch table
The problem was that it was fugly and no longer resembled a coroutine. I figured if I put it inside a module (see below), it would hide all the nasty stuff and maybe even make it more useable.
package Iterator; sub TIESCALAR { bless $_[1], $_[0] } sub STORE {} sub FETCH { $_[0]->[1] = 0 if ! $_[0]->[1] || $_[0]->[1] == @{ $_[0]-> +[2] }; $_[0]->[1]++ } package Coroutine; sub new { my $self = bless [[[]]], $_[0]; tie $self->[0][0], 'Iterator', $self->[0]; return $self; } sub add_section { push @{ $_[0]->[0][2] } , $_[1] } sub create { my $s = shift; return sub { $s->[0][2][ $s->[0][0] ]->($s +, @_) } } 42;
Boy was I wrong! It came out horrendous. What, you don't believe me? Just look:
#!/usr/bin/perl use strict; use warnings; use Coroutine; my $coroutine = Coroutine->new(); $coroutine->add_section( sub { my $self = shift; $self->[3] = shift; $self->[4] = \@_; print "$_\n" for @{ $self->[4] }; return $self->[3]++; } ); $coroutine->add_section( sub { my $self = shift; print "$self->[3]\n"; return rand() > .5 ? 'weird' : ++$self->[3]; } ); $coroutine->add_section( sub { my $self = shift; print "The end is near - goodbye cruel "; return pop @{ $self->[4] }; } ); my $wacky = $coroutine->create(42, 'hello', 'world'); print $wacky->(42, 'hello', 'world'), "\n"; print $wacky->(), "\n"; print $wacky->(), "\n"; print $wacky->(), "\n";
All of that to do the following if Perl natively supported coroutines:
coroutine create { my $foo = shift; my @bar = @_; print "$_\n" for @bar; yield $foo++; print "$foo\n"; yield rand() > .5 ? 'weird' : ++$foo; print "The end is near - goodbye cruel "; yield pop @bar; } my $wacky = create(42, 'hello', 'world'); print $wacky->(42, 'hello', 'world'), "\n"; print $wacky->(), "\n"; print $wacky->(), "\n"; print $wacky->(), "\n"; __END__ hello world 42 43 44|weird The end is near - goodbye cruel world 0
Incidently, revdiablo and I came up with a solution using evil gotos and source filters about a week ago that was elegant.
So why post? Well even with the explanation, it isn't easy to see what is going on inside the module - especially with the bless/tie combo. I really didn't intend it to come out obfu. Sometimes obfu just happens
Cheers - L~R
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Coroutines
by tilly (Archbishop) on Aug 22, 2004 at 05:18 UTC | |
Re: Coroutines
by Zaxo (Archbishop) on Aug 22, 2004 at 05:11 UTC | |
by Limbic~Region (Chancellor) on Aug 22, 2004 at 05:14 UTC | |
(non-)Obfu Coroutines
by Roy Johnson (Monsignor) on Nov 16, 2005 at 19:32 UTC |