File Coverage

blib/lib/Sub/Lambda.pm
Criterion Covered Total %
statement 28 28 100.0
branch 3 4 75.0
condition n/a
subroutine 10 10 100.0
pod 1 1 100.0
total 42 43 97.6


line stmt bran cond sub pod time code
1             # (hack) the next line produces an eval that works in *main::* lexical scope:
2 2     2   416 *Sub::Lambda::evaluate = sub { eval $_[0] };
3              
4 2     2   38495 use strict;
  2         3  
  2         78  
5              
6             package Sub::Lambda;
7 2     2   1892 use Memoize;
  2         5523  
  2         103  
8              
9 2     2   16 use base qw(Exporter);
  2         8  
  2         198  
10             our @EXPORT = qw(fn ap);
11              
12 2     2   12 use vars qw($VERSION);
  2         2  
  2         1031  
13             $VERSION = '0.02';
14              
15             =head1 NAME
16              
17             Sub::Lambda - syntactic sugar for lambdas in Perl
18              
19              
20             =head1 SYNOPSIS
21              
22             use Sub::Lambda;
23              
24             *plus = fn a => fn b => '$a + $b';
25             my $minus = fn a => fn b => q{ $a - $b };
26             *flip = fn f => fn a => fn b => ap qw(f b a);
27             *sum = fn h => -t => q{ @t ? $h+sum(@t) : ($h || 0) };
28              
29             print plus(1)->(2) . "\n"; # 3 = 1 + 2
30             print $minus->(10)->(5) . "\n"; # 5 = 10 - 5
31             print flip($minus)->(10)->(5) . "\n"; # -5 = 5 - 10
32             print sum(1,2,3,4) . "\n"; # 10 = 1+2+3+4
33              
34             my $fac = fn f => fn n => q{ ($n<1) ? 1 : $n*$f->($n-1) };
35              
36             my $Y = fn m => ap(
37             (fn f => ap m => fn a => ap f => f => a => ()) =>
38             (fn f => ap m => fn a => ap f => f => a => ())
39             );
40              
41             print $Y->($fac)->(5) . "\n"; # 120 = 5!
42              
43              
44             =head1 DESCRIPTION
45              
46             This module provides syntactic sugar for lambda abstractions and
47             applications. Perl supports lambdas through subroutine
48             references. You can write things like the curried addition:
49              
50             sub { my ($x) = @_; sub { my ($y) = @_; $x + $y } }
51              
52             However, this is not very convenient nor readable for more involved lambda
53             expressions. Contrast this with the sugared syntax for the same function:
54              
55             fn x => fn y => q{ $x + $y }
56              
57             If you would like even more convenience at the expense of somewhat unclear
58             semantics, check out the experimental L module, with
59             which you could write:
60              
61             ( \x -> \y -> {$x+$y} )
62            
63              
64              
65             =head2 METHODS
66              
67             =over
68             =cut
69              
70 17     17   88 sub _neat ($) { /^\-?[a-zA-Z]\w*$/ };
71 13     13   22 sub _var ($) { local $_=$_[0]; s/^/\$/; s/^\$\-/@/; $_ }
  13         40  
  13         21  
  13         43  
72 5     5   11 sub _vars (@) { map {_var $_} grep {_neat $_} @_ }
  5         11  
  5         11  
73 22     22   72 sub _expr (@) { '(' . (join ',', @_) . ')' }
74              
75             =item fn(pattern => q{ body })
76              
77             Models lambda abstraction. In list context, outputs the Perl code for
78             a lambda with a given pattern and body. In scalar context, returns a
79             subroutine reference. This context trick allows the sub to be compiled in a
80             one-step B, which appears to be necessary to make sure Perl
81             gets the variable scoping right. Note that this means the end user has
82             to make sure that the functions are called in
83             a scalar context! When in doubt, use C.
84              
85             The basic pattern is just a single variable name; an incrementor can
86             be written as:
87            
88             fn(x => '$x + 1')->(1) # =2
89              
90             Prefixing with a dash captures lists of arguments:
91              
92             fn(-x => 'scalar(@x)')->(1,2,3,4,5) #= 5
93              
94             Multiple arguments are allowed too:
95              
96             (fn qw(a b) => '$a+$b')->(1, 2) #= 3
97             (fn qw(h -t) => '{$h=>[@t]}')->(1,2,3,4) #= {1 => [2,3,4]}
98              
99             Currying is possible too:
100              
101             (fn a => fn b => '$a+$b')->(1)->(2) #= 3
102              
103             Here are some translation examples:
104              
105             Scheme Perl
106              
107             (lambda (x) (f x)) (fn x => 'f($x)')
108              
109             (lambda x (f x)) (fn -x => 'f(\@x)')
110              
111             (lambda (x y z) (f x y z)) (fn x => y => z => q{ f($x,$y,$z) })
112              
113             (lambda (h . t) (f h t)) (fn h => -t => q{ f($h, \@t) })
114              
115             ...
116              
117             Haskell
118              
119             \f -> \a -> \b -> f b a (fn f=>fn a=>fn b=>q{$f->($b)->($a)})
120              
121             B nesting in the following way:
122              
123             fn a => q{ fn b => '$a+$b' }
124              
125             In this example C<$a+$b> will be compiled outside of the lexical scope
126             where C was defined, hence the function will not work.
127              
128             =cut
129              
130             sub fn (@) {
131             my $body = pop;
132             my $tmpl = _expr _vars @_;
133             my $code = qq{sub { my $tmpl = \@_; $body }};
134             return wantarray ? $code : evaluate($code);
135             }
136              
137              
138             =item ap(@expressions)
139              
140             Models application. Applies the given expressions to the left,
141             as if with Haskell C. In list context, it generates Perl code,
142             while in scalar context it B's it:
143              
144             print ap(qw(a b c)) # ($a)->($b)->(scalar($c));
145              
146             The expressions can be pieces of Perl code or neat variable names
147             (C standing for C<$x> and C<-x> for C<@x>).
148              
149             C is useful as a shorthand in cases like this:
150              
151             fn f => fn a => fn b => q{ $f->($b)->($a) }
152              
153             Expressed with C it reads:
154              
155             fn f => fn a => fn b => ap qw(f b a)
156              
157             With B and parentheses one can write arbitrarily complex
158             lambda expressions.
159              
160             =cut
161              
162             sub ap (@) {
163 5 100   5 1 84 my @args = map {_neat $_ ? _var $_ : $_} @_;
  12         20  
164 5         12 $args[-1] = "scalar" . _expr $args[-1];
165 5         8 my $code = join "->", map { _expr $_ } @args;
  12         19  
166 5 50       127 return wantarray ? $code : evaluate($code);
167             }
168              
169             memoize('fn'); # speedup re-compilations
170              
171             1;
172              
173             __END__