Pages: Welcome | Projects

Perl callbacks and locals

2015/1/11
Tags: [ perl ]

If you have a functional background you cannot live without your map and filter. They are elegant, they are simple, they are one of the things which IMHO Java misses more.

Perl has got map and grep, the second being essentially a filter (the name comes definitely from the Unix shell world). You start using them, and eventually you want to do your own function accepting a block. How can you do that?

The perlsub document gives you an answer: you can use Function prototypes. They allow you to override the default parameter expansion of Perl. And if you do it in a sensible way, you can make people very happy, including yourself.

So, it turns out that the & symbol allows a block to be passed as actual parameter, having a code reference on the other side. Totally sweet. I especially loved the crazy try-catch definition (but I'll let you seek for it, your head will explode :D)

So far so good, I created my own reduce, which is kind of improper, as not really implemented as recursive call...

#!/usr/bin/perl -w

use strict;
use warnings;
use feature qw(say);


sub reduce (&@) {
  my $f = shift;
  my $acc;

  for my $x (@_) {
    if (defined $acc) {
      $acc = $f->($acc, $x);
    } else {
      $acc = $x;
    }
  }

  $acc;
}

my @a = (1, 2, 3, 4);
my $sum = reduce { $_[0] + $_[1] } @a;
say $sum;

Guess what, we've got 10 as expected. But, but, but... that $_[0] and $_[1] thing is not so perlish. To obviate we can define the two special package variables $a and $b, as people expect while using the sort function.

You see, this is a nice use case for the local keyword, which enables as safe and nice dynamic binding of the two variables. You don't have actual parameters, and you don't need global variables.

#!/usr/bin/perl -w

use strict;
use warnings;
use feature qw(say);


sub reduce (&@) {
  my $f = shift;
  my $acc;

  for my $x (@_) {
    if (defined $acc) {
      local ($a, $b) = ($acc, $x);
      $acc = &$f;
    } else {
      $acc = $x;
    }
  }

  $acc;
}

my @a = (1, 2, 3, 4);
my $sum = reduce { $a + $b } @a;
say $sum;

Run that... 10 again. Perl is so funny.