search.cpan.org is shutting down
Anton Tayanovskyy > Sub-Lambda-0.02 > Sub::Lambda
Module Version: 0.02

# NAME

Sub::Lambda - syntactic sugar for lambdas in Perl

# SYNOPSIS

use Sub::Lambda;

*plus     = fn a => fn b => '\$a + \$b';
my \$minus = fn a => fn b => q{ \$a - \$b };
*flip     = fn f => fn a => fn b => ap qw(f b a);
*sum      = fn h => -t => q{ @t ? \$h+sum(@t) : (\$h || 0) };

print plus(1)->(2)            . "\n";  # 3  = 1 + 2
print \$minus->(10)->(5)       . "\n";  # 5  = 10 - 5
print flip(\$minus)->(10)->(5) . "\n";  # -5 = 5 - 10
print sum(1,2,3,4)            . "\n";  # 10 = 1+2+3+4

my \$fac = fn f => fn n => q{ (\$n<1) ? 1 : \$n*\$f->(\$n-1) };

my \$Y   = fn m => ap(
(fn f => ap m => fn a => ap f => f => a => ()) =>
(fn f => ap m => fn a => ap f => f => a => ())
);

print \$Y->(\$fac)->(5) . "\n";  # 120 = 5!

# DESCRIPTION

This module provides syntactic sugar for lambda abstractions and applications. Perl supports lambdas through subroutine references. You can write things like the curried addition:

sub { my (\$x) = @_; sub { my (\$y) = @_; \$x + \$y } }

However, this is not very convenient nor readable for more involved lambda expressions. Contrast this with the sugared syntax for the same function:

fn x => fn y => q{ \$x + \$y }

If you would like even more convenience at the expense of somewhat unclear semantics, check out the experimental Sub::Lambda::Filter module, with which you could write:

( \x -> \y -> {\$x+\$y} )

## METHODS

fn(pattern => q{ body })

Models lambda abstraction. In list context, outputs the Perl code for a lambda with a given pattern and body. In scalar context, returns a subroutine reference. This context trick allows the sub to be compiled in a one-step eval, which appears to be necessary to make sure Perl gets the variable scoping right. Note that this means the end user has to make sure that the functions are called in a scalar context! When in doubt, use scalar().

The basic pattern is just a single variable name; an incrementor can be written as:

fn(x => '\$x + 1')->(1) # =2

Prefixing with a dash captures lists of arguments:

fn(-x => 'scalar(@x)')->(1,2,3,4,5) #= 5

Multiple arguments are allowed too:

(fn qw(a b)  => '\$a+\$b')->(1, 2)          #= 3
(fn qw(h -t) => '{\$h=>[@t]}')->(1,2,3,4)  #= {1 => [2,3,4]}

Currying is possible too:

(fn a => fn b => '\$a+\$b')->(1)->(2)       #= 3

Here are some translation examples:

Scheme                      Perl

(lambda (x) (f x))          (fn  x => 'f(\$x)')

(lambda x (f x))            (fn -x => 'f(\@x)')

(lambda (x y z) (f x y z))  (fn  x => y => z => q{ f(\$x,\$y,\$z) })

(lambda (h . t) (f h t))    (fn  h => -t => q{ f(\$h, \@t) })

...

\f -> \a -> \b -> f b a     (fn f=>fn a=>fn b=>q{\$f->(\$b)->(\$a)})

AVOID nesting in the following way:

fn a => q{ fn b => '\$a+\$b' }

In this example \$a+\$b will be compiled outside of the lexical scope where a was defined, hence the function will not work.

ap(@expressions)

Models application. Applies the given expressions to the left, as if with Haskell foldl1 (\$). In list context, it generates Perl code, while in scalar context it eval's it:

print ap(qw(a b c)) # (\$a)->(\$b)->(scalar(\$c));

The expressions can be pieces of Perl code or neat variable names (x standing for \$x and -x for @x).

ap is useful as a shorthand in cases like this:

fn f => fn a => fn b => q{ \$f->(\$b)->(\$a) }

fn f => fn a => fn b => ap qw(f b a)

With ap and parentheses one can write arbitrarily complex lambda expressions.

## THE Y COMBINATOR

To take a large example, let us write down the applicative-order Y combinator and a factorial function (for derivation and rationale see Richard P. Gabriel's The Why of Y, available online http://www.dreamsongs.com/NewFiles/WhyOfY.pdf).

Y = \m -> (\f -> m (\a -> f f a)) (\f -> m (\a -> f f a))

In Perl with Sub::Lambda:

my \$Y = fn m => ap(
(fn f => ap m => fn a => ap qw(f f a)) =>
(fn f => ap m => fn a => ap qw(f f a))
);

In standard Perl (compare with the above):

my \$Y = sub {
my (\$m) = @_;
( sub { my (\$f) = @_;
\$m->(sub { my (\$a) = @_;
\$f->(\$f)->(\$a);
}) } )->(
sub { my (\$f) = @_;
\$m->(sub { my (\$a) = @_;
\$f->(\$f)->(\$a); })})};

Factorial function:

my \$fac = fn f => fn n => q{ (\$n<1) ? 1 : \$n*\$f->(\$n-1) };

Testing it:

print \$Y->(\$fac)->(5);  # prints 120=5!

## SPEED

To avoid repeated compilations fn is memoized. This only improves performance where lambdas are constructed repeatedly, as in loops. The performance gains are modest.

fn ap

# AUTHOR

Anton Tayanovskyy <anton.tayanovskyy at gmail.com>

# ACKNOWLEDGEMENTS

Big thanks to Eugene Grigoriev <eugene.grigoriev at gmail.com> for his help, ideas and feedback.