2013/12/22

Futures advent day 22

Day 22 - Equivalence of Control Flow

Over the past twenty or so posts we have seen many examples of control-flow structures using Futures to help write possibly-asynchronous, possibly-concurrent code in simple, neat ways which mirror the regular kinds of synchronous control flow that Perl already provides. You may by now have come to the conclusion that it is possible to duplicate any kind of control-flow logic using futures.

Simple sequencing of one operation then the next is done using then:

FIRST();
SECOND();
THIRD();
FIRST()
  ->then(sub { SECOND() })
  ->then(sub { THIRD() });

Sequencing is interrupted by thrown exceptions, which may be caught using else:

try { FIRST();
      SECOND();
} catch {
      CATCH();
};
FIRST()
  ->then(sub { SECOND () })
  ->else(sub {
     CATCH()
  });

Conditional execution can be performed by using a regular if inside a then_with_f sequence, to return either a new or the original future:

FIRST();

if( $COND ) {
  SECOND();
}
FIRST()
  ->then_with_f(sub { my ( $f ) = @_;
    if( $COND ) {
      return SECOND() }
    return $f; });

Repeated loops such as do {} while can be implemented using Future::Utils::repeat():

do {
  BODY()
} while( $COND )
repeat {
  BODY();
} while => sub { $COND };

A pre-condition while {} loop is a little trickier because it still needs a future to return if the loop body doesn't execute at all. The simplest way is simply to skip the repeat call if it isn't required:

 
while( $COND ) {
  BODY();
}
!$COND ? Future->new->done()
    : repeat {
        BODY();
      } while => sub { $COND };

A foreach {} loop can also be written using repeat:

foreach my $VAR ( @LIST ) {
  BODY();
}
repeat { my $VAR = shift;
  BODY();
} foreach => [ @LIST ];

The values generated by a map {} call can be created using Future::Utils::fmap:

my @values = map {
  BODY();
} @LIST;
my $value_f = fmap {
  BODY();
} foreach => [ @LIST ];

In every case here, the future version of the control flow structure yields a future, which of course can then be combined inside other structures as required:

do {
  FIRST();
  foreach ( @LIST ) {
    SECOND($_);
  }
  if( $C ) {
    THIRD();
  }
} until( $HAPPY )
repeat {
  FIRST()
    ->then(sub { repeat {
      SECOND(shift);
    } foreach => [ @LIST ] })
    ->then_with_f(sub { my ( $f ) = @_;
      if( $C ) { return THIRD(); }
      return $f; });
} until => sub { $HAPPY };

<< First | < Prev | Next >


Edit 2013/12/29: Updated to use then_with_f

No comments:

Post a Comment

Post a Comment