Sawyer
Sawyer

Reputation: 15917

How can I do function partial application in Perl?

Is there any way to achieve partial application in Perl?

Suppose, I want to do something like:

sub each_file($arr, $op) {
    $op->($_) for @{$arr};
    ...
} 

sub each_line($op, $file) {
    ... 
}

each_file($arr, each_line($op));

I want to partially apply each_line() to only $op, so it'll become a new function can be passed to $each_file, how do I express this in idiomatic Perl?

Upvotes: 2

Views: 487

Answers (3)

Chris
Chris

Reputation: 1697

You can roll this functionality up into a class. Then you can overload the subroutine dereference operator to make it look like your class is really a code reference.

package Partial;

use overload '&{}' => \&call;

sub new {
    my $class = shift;
    my $code = shift;
    bless {code => $code, args => \@_}, $class;
}

sub call {
    my ($self) = @_;
    return sub{ $self->{code}->(@{$self->{args}}, @_) }
}

You can then use it like this:

sub printArgs {
    print join ", ", @_;
    print "\n";
}

my $partial = Partial->new(\&printArgs, 'foo', 'bar');
$partial->('baz', 'bat');
# prints foo, bar, baz, bat

Upvotes: 0

Neil Slater
Neil Slater

Reputation: 27207

You can do this in Perl with two approaches combined:

  • A function which returns a function reference

  • Closures

Example:

sub each_file {
    my ($arr, $line_fn) = @_;
    $line_fn->($_) for @{$arr};
    ...
} 

sub each_line {
    my ($op, $file) = @_;
    ... 
}

sub make_line_processor {
  my ( $op ) = @_;

  # This is closed over $op, which effectively becomes 
  # a constant for the returned function
  my $fn = sub {
     return each_line( $op, @_ );
  };
  return $fn;
}


# To call it:
each_file( $arr, make_line_processor($op) );

This can be an even more useful technique in cases where you don't want $op directly, but some expensive-to-fetch derivation of it. In which case you would calculate the derived value just once (in the make_line_processor function) and close over that instead.

Upvotes: 2

Lumi
Lumi

Reputation: 15264

# given some $op as implied by your code snippet
each_file($arr, sub { each_line($op, shift) });
# shift op will be applied when anonymous sub { … } is called

(Your code snippet doesn't make it entirely clear what you intend $op to be when you make the call to each_line. It's usually better to present small working programs.)

Upvotes: 1

Related Questions