Hi all,

I'm more-or-less thinking out loud for a bit here. I'd appreciate any
thoughts or comments people might want to make.


So, I have the CPS module:

  http://search.cpan.org/~pevans/CPS-0.04/lib/CPS.pm

On its own it doesn't create an awful lot of real interest yet; nothing
much that can't easily be done with "normal perl". But this wasn't yet
the point.

I'm thinking of ways to combine it nicely with IO::Async (or other event
frameworks, but I'll take this as a useful example).

My primary thought is how to combine things like kwhile() with other
events; ideally it'd be nice to interleave kwhile() loops with other
event processing. ((I pick on kwhile here specifically, because all the
other k*() control-flow structures are built on kwhile(), so getting that
right gives the others for free)).

Imagine the normal while loop with some event handling:

  while( cond ) {
    do_something();
    process_pending_events();
  }

What if instead we had an IO::Async-aware version of CPS. Lets say, for
the sake of argument, we had some sort of object, which knows of the
IO::Async loop.. lets call it, for reasons that'll become clear soon, a
Governor:

  my $governor = CPS::Governor::IOAsync->new( $loop );

  $governor->kwhile( sub {
    my ( $knext, $kdone ) = @_;

    return $kdone->() if !cond;

    do_something( on_done => $knext );

  }, sub { say "Finished" } );

Aside from the slightly messy syntax, this is a fairly short and
convenient way to mix CPS with IO::Async. The Governor object can mix
iterations of the while loop with other event processing in a neat simple
way.

In fact, given as it can store state, we can configure it more powerfully
than just this. Suppose we're processing a big list of, maybe, hundreds
of thousands of little items. Each item is tiny, but processing the whole
list is going to take several seconds of CPU time, during which it would
be polite if we handled IO events (such as GUI interactions). No problem..

  my @items = ("foo") x 1_000_000;

  my $governor = CPS::Governor::IOAsync->new( $loop, iterations => 100 );

  $governor->kforeach( \...@items, sub {
    my ( $item, $knext ) = @_;
    process( $item );
    goto &$knext;
  }, sub { say "Finished" } );

The Governor object here takes care of the iteration counting. Every 100
iterations it'll pause a moment and check IO::Async pending events.

The way this would be implemented would be that the Governor's kwhile()
method would use the $loop's idle event handling to call the next
iteration, rather than invoke it immediately. This also lets us do such
interesting things as:

  my @numbers = ( 1 .. 10 );

  my $governor = CPS::Governor::IOAsync->new( $loop, iterations => 2 );

  $governor->kforeach( \...@numbers, sub {
    print "A$_[0] "; goto &$_[1]
  }, sub {} );

  $governor->kforeach( \...@numbers, sub {
    print "B$_[0] "; goto &$_[1]
  }, sub {} );

This would print

  A1 A2 B1 B2 A3 A4 B3 B4 A5 A6 B5 B6 A7 A8 B7 B8 A9 A10 B9 B10


The only real point of hesitation in my mind, though, is that of
implementing control flow structures as methods on an object. This feels
somehow unclean.

Perhaps instead they can use the new lexical pragmas in 5.10; allowing a
somewhat cleaner style of:

  use CPS qw( kforeach );

  # kforeach here would use the "default" flat Governor

  {
    use CPS::Governor::IOAsync $loop, iterations => 10;

    kforeach( [ 1 .. 100 ], sub {
      my ( $item, $knext ) = @_;

      do_item( $item, on_done => $knext );
    }, sub { say "Finished" } );
  }

Or perhaps being a pragma it ought to be all lowercase? What are the
rules here?

And finally there's nothing IO::Async'y about the whole thing. Simply
implementing another kwhile() method would allow Governors such as

  use CPS::Governor::POE $kernel;
  use CPS::Governor::AnyEvent $loop;
  use CPS::Governor::GLib $maincontext;
  ...

-- 
Paul "LeoNerd" Evans

leon...@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

Attachment: signature.asc
Description: PGP signature

Reply via email to