File Coverage

blib/lib/CPS/Functional.pm
Criterion Covered Total %
statement 88 90 97.7
branch 24 38 63.1
condition n/a
subroutine 21 21 100.0
pod 0 5 0.0
total 133 154 86.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2010 -- leonerd@leonerd.org.uk
5              
6             package CPS::Functional;
7              
8 5     5   219260 use strict;
  5         38  
  5         118  
9 5     5   22 use warnings;
  5         7  
  5         153  
10              
11             our $VERSION = '0.19';
12              
13 5     5   22 use Carp;
  5         6  
  5         218  
14              
15 5     5   22 use Exporter 'import';
  5         6  
  5         175  
16              
17 5     5   1433 use CPS qw( gkloop );
  5         11  
  5         615  
18              
19             our @CPS_PRIMS = qw(
20             kmap
21             kgrep
22             kfoldl kfoldr
23             kunfold
24             );
25              
26             our @EXPORT_OK = (
27             @CPS_PRIMS,
28             map( "g$_", @CPS_PRIMS ),
29             );
30              
31             # Don't hard-depend on Sub::Name since it's only a niceness for stack traces
32             BEGIN {
33 5 50   5   16 if( eval { require Sub::Name } ) {
  5         37  
34 5         4443 *subname = \&Sub::Name::subname;
35             }
36             else {
37             # Ignore the name, return the CODEref
38 0         0 *subname = sub { return $_[1] };
  0         0  
39             }
40             }
41              
42             =head1 NAME
43              
44             C - functional utilities in Continuation-Passing Style
45              
46             =head1 SYNOPSIS
47              
48             use CPS::Functional qw( kmap );
49              
50             use Example::HTTP::Client qw( k_get_http );
51             use List::Util qw( sum );
52              
53             my @URLs = (
54             "http://www.foo.com",
55             "http://www.bar.com",
56             );
57              
58             kmap( \@URLs,
59             sub {
60             my ( $item, $kret ) = @_;
61              
62             k_get_http( uri => $item, on_response => sub {
63             my ( $response ) = @_;
64              
65             $kret->( $response->content_length );
66             } );
67             },
68             sub {
69             my ( @sizes ) = @_;
70              
71             say "Total length of all URLs: " . sum(@sizes);
72             },
73             );
74              
75             =head1 DESCRIPTION
76              
77             This module provides L versions of data-flow functionals, such as Perl's
78             C and C, where function bodies are invoked and expected to return
79             data, which the functional manages. They are built on top of the control-flow
80             functionals provided by the C module itself.
81              
82             =cut
83              
84             =head1 FUNCTIONS
85              
86             =cut
87              
88             =head2 kmap( \@items, \&body, $k )
89              
90             CPS version of perl's C statement. Calls the C code once for each
91             element in C<@items>, capturing the list of values the body passes into its
92             continuation. When the items are exhausted, C<$k> is invoked and passed a list
93             of all the collected values.
94              
95             $body->( $item, $kret )
96             $kret->( @items_out )
97              
98             $k->( @all_items_out )
99              
100             =cut
101              
102             sub gkmap
103             {
104 1     1 0 3 my ( $gov, $items, $body, $k ) = @_;
105              
106 1 50       5 ref $items eq "ARRAY" or croak 'Expected $items as ARRAY ref';
107 1 50       3 ref $body eq "CODE" or croak 'Expected $body as CODE ref';
108              
109 1         2 my @ret;
110 1         1 my $idx = 0;
111              
112             gkloop( $gov,
113             sub {
114 4     4   6 my ( $knext, $klast ) = @_;
115 4 100       9 goto &$klast unless $idx < scalar @$items;
116             @_ = (
117             $items->[$idx++],
118 3         11 sub { push @ret, @_; goto &$knext }
  3         6  
119 3         11 );
120 3         6 goto &$body;
121             },
122 1     1   3 sub { $k->( @ret ) },
123 1         7 );
124             }
125              
126             =head2 kgrep( \@items, \&body, $k )
127              
128             CPS version of perl's C statement. Calls the C code once for each
129             element in C<@items>, capturing those elements where the body's continuation
130             was invoked with a true value. When the items are exhausted, C<$k> is invoked
131             and passed a list of the subset of C<@items> which were selected.
132              
133             $body->( $item, $kret )
134             $kret->( $select )
135              
136             $k->( @chosen_items )
137              
138             =cut
139              
140             sub gkgrep
141             {
142 1     1 0 3 my ( $gov, $items, $body, $k ) = @_;
143              
144 1 50       4 ref $items eq "ARRAY" or croak 'Expected $items as ARRAY ref';
145 1 50       3 ref $body eq "CODE" or croak 'Expected $body as CODE ref';
146              
147 1         2 my @ret;
148 1         1 my $idx = 0;
149              
150             gkloop( $gov,
151             sub {
152 5     5   9 my ( $knext, $klast ) = @_;
153 5 100       9 goto &$klast unless $idx < scalar @$items;
154 4         6 my $item = $items->[$idx++];
155             @_ = (
156             $item,
157 4 100       19 sub { push @ret, $item if $_[0]; goto &$knext }
  4         9  
158 4         11 );
159 4         9 goto &$body;
160             },
161 1     1   2 sub { $k->( @ret ) },
162 1         8 );
163             }
164              
165             =head2 kfoldl( \@items, \&body, $k )
166              
167             CPS version of C, which collapses (or "folds") a list of
168             values down to a single scalar, by successively accumulating values together.
169              
170             If C<@items> is empty, invokes C<$k> immediately, passing in C.
171              
172             If C<@items> contains a single value, invokes C<$k> immediately, passing in
173             just that single value.
174              
175             Otherwise, initialises an accumulator variable with the first value in
176             C<@items>, then for each additional item, invokes the C passing in the
177             accumulator and the next item, storing back into the accumulator the value
178             that C passed to its continuation. When the C<@items> are exhausted, it
179             invokes C<$k>, passing in the final value of the accumulator.
180              
181             $body->( $acc, $item, $kret )
182             $kret->( $new_acc )
183              
184             $k->( $final_acc )
185              
186             Technically, this is not a true Scheme/Haskell-style C, as it does not
187             take an initial value. (It is what Haskell calls C.) However, if such
188             an initial value is required, this can be provided by
189              
190             kfoldl( [ $initial, @items ], \&body, $k )
191              
192             =cut
193              
194             sub gkfoldl
195             {
196 1     1 0 3 my ( $gov, $items, $body, $k ) = @_;
197              
198 1 50       3 ref $items eq "ARRAY" or croak 'Expected $items as ARRAY ref';
199 1 50       3 ref $body eq "CODE" or croak 'Expected $body as CODE ref';
200              
201 1 50       4 $k->( undef ), return if @$items == 0;
202 1 50       2 $k->( $items->[0] ), return if @$items == 1;
203              
204 1         2 my $idx = 0;
205 1         2 my $acc = $items->[$idx++];
206              
207             gkloop( $gov,
208             sub {
209 3     3   5 my ( $knext, $klast ) = @_;
210 3 100       7 goto &$klast unless $idx < scalar @$items;
211             @_ = (
212             $acc,
213             $items->[$idx++],
214 2         13 sub { $acc = shift; goto &$knext }
  2         3  
215 2         6 );
216 2         5 goto &$body;
217             },
218 1     1   3 sub { $k->( $acc ) },
219 1         8 );
220             }
221              
222             =head2 kfoldr( \@items, \&body, $k )
223              
224             A right-associative version of C. Where C starts with the
225             first two elements in C<@items> and works forward, C starts with the
226             last two and works backward.
227              
228             $body->( $item, $acc, $kret )
229             $kret->( $new_acc )
230              
231             $k->( $final_acc )
232              
233             As before, an initial value can be provided by modifying the C<@items> array,
234             though note it has to be last this time:
235              
236             kfoldr( [ @items, $initial ], \&body, $k )
237              
238             =cut
239              
240             sub gkfoldr
241             {
242 1     1 0 46 my ( $gov, $items, $body, $k ) = @_;
243              
244 1 50       6 ref $items eq "ARRAY" or croak 'Expected $items as ARRAY ref';
245 1 50       3 ref $body eq "CODE" or croak 'Expected $body as CODE ref';
246              
247 1 50       4 $k->( undef ), return if @$items == 0;
248 1 50       3 $k->( $items->[0] ), return if @$items == 1;
249              
250 1         2 my $idx = scalar(@$items) - 1;
251 1         2 my $acc = $items->[$idx--];
252              
253             gkloop( $gov,
254             sub {
255 3     3   7 my ( $knext, $klast ) = @_;
256 3 100       6 goto &$klast if $idx < 0;
257             @_ = (
258             $items->[$idx--],
259             $acc,
260 2         11 sub { $acc = shift; goto &$knext }
  2         4  
261 2         7 );
262 2         5 goto &$body;
263             },
264 1     1   3 sub { $k->( $acc ) },
265 1         7 );
266             }
267              
268             =head2 kunfold( $seed, \&body, $k )
269              
270             An inverse operation to C; turns a single scalar into a list of
271             items. Repeatedly calls the C code, capturing the values it returns,
272             until it indicates the end of the loop, then invoke C<$k> with the collected
273             values.
274              
275             $body->( $seed, $kmore, $kdone )
276             $kmore->( $new_seed, @items )
277             $kdone->( @items )
278              
279             $k->( @all_items )
280              
281             With each iteration, the C is invoked and passed the current C<$seed>
282             value and two continuations, C<$kmore> and C<$kdone>. If C<$kmore> is invoked,
283             the passed items, if any, are appended to the eventual result list. The
284             C is then re-invoked with the new C<$seed> value. If C<$klast> is
285             invoked, the passed items, if any, are appended to the return list, then the
286             entire list is passed to C<$k>.
287              
288             =cut
289              
290             sub gkunfold
291             {
292 1     1 0 3 my ( $gov, $seed, $body, $k ) = @_;
293              
294 1 50       4 ref $body eq "CODE" or croak 'Expected $body as CODE ref';
295              
296 1         2 my @ret;
297              
298             gkloop( $gov,
299             sub {
300 5     5   6 my ( $knext, $klast ) = @_;
301             @_ = (
302             $seed,
303 4         21 sub { $seed = shift; push @ret, @_; goto &$knext },
  4         6  
  4         6  
304 1         5 sub { push @ret, @_; goto &$klast },
  1         1  
305 5         25 );
306 5         10 goto &$body;
307             },
308 1     1   3 sub { $k->( @ret ) },
309 1         8 );
310             }
311              
312             CPS::_governate "g$_" => $_ for @CPS_PRIMS;
313              
314             =head1 EXAMPLES
315              
316             The following aren't necessarily examples of code which would be found in real
317             programs, but instead, demonstrations of how to use the above functions as
318             ways of controlling program flow.
319              
320             Without dragging in large amount of detail on an asynchronous or event-driven
321             framework, it is difficult to give a useful example of behaviour that CPS
322             allows that couldn't be done just as easily without. Nevertheless, I hope the
323             following examples will be useful to demonstrate use of the above functions,
324             in a way which hints at their use in a real program.
325              
326             =head2 Implementing C using C
327              
328             use CPS::Functional qw( kfoldl );
329              
330             my @words = qw( My message here );
331              
332             kfoldl(
333             \@words,
334             sub {
335             my ( $left, $right, $k ) = @_;
336              
337             $k->( "$left $right" );
338             },
339             sub {
340             my ( $str ) = @_;
341              
342             print "Joined up words: $str\n";
343             }
344             );
345              
346             =head2 Implementing C using C
347              
348             The following program illustrates the way that C can split a
349             string, in a reverse way to the way C can join it.
350              
351             use CPS::Functional qw( kunfold );
352              
353             my $str = "My message here";
354              
355             kunfold(
356             $str,
357             sub {
358             my ( $s, $kmore, $kdone ) = @_;
359              
360             if( $s =~ s/^(.*?) // ) {
361             return $kmore->( $s, $1 );
362             }
363             else {
364             return $kdone->( $s );
365             }
366             },
367             sub {
368             my @words = @_;
369             print "Words in message:\n";
370             print "$_\n" for @words;
371             }
372             );
373              
374             =head2 Generating Prime Numbers
375              
376             While the design of C is symmetric to C, the seed value
377             doesn't have to be successively broken apart into pieces. Another valid use
378             for it may be storing intermediate values in computation, such as in this
379             example, storing a list of known primes, to help generate the next one:
380              
381             use CPS::Functional qw( kunfold );
382            
383             kunfold(
384             [ 2, 3 ],
385             sub {
386             my ( $vals, $kmore, $kdone ) = @_;
387            
388             return $kdone->() if @$vals >= 50;
389            
390             PRIME: for( my $n = $vals->[-1] + 2; ; $n += 2 ) {
391             $n % $_ == 0 and next PRIME for @$vals;
392            
393             push @$vals, $n;
394             return $kmore->( $vals, $n );
395             }
396             },
397             sub {
398             my @primes = ( 2, 3, @_ );
399             print "Primes are @primes\n";
400             }
401             );
402              
403             =head2 Forward-reading Program Flow
404              
405             One side benefit of the CPS control-flow methods which is unassociated with
406             asynchronous operation, is that the flow of data reads in a more natural
407             left-to-right direction, instead of the right-to-left flow in functional
408             style. Compare
409              
410             sub square { $_ * $_ }
411             sub add { $a + $b }
412              
413             print reduce( \&add, map( square, primes(10) ) );
414              
415             (because C is a language builtin but C is a function with C<(&)>
416             prototype, it has a different way to pass in the named functions)
417              
418             with
419              
420             my $ksquare = liftk { $_[0] * $_[0] };
421             my $kadd = liftk { $_[0] + $_[1] };
422              
423             kprimes 10, sub {
424             kmap \@_, $ksquare, sub {
425             kfoldl \@_, $kadd, sub {
426             print $_[0];
427             }
428             }
429             };
430              
431             This translates roughly to a functional vs imperative way to describe the
432             problem:
433              
434             Print the sum of the squares of the first 10 primes.
435              
436             Take the first 10 primes. Square them. Sum them. Print.
437              
438             Admittedly the closure creation somewhat clouds the point in this small
439             example, but in a larger example, the real problem-solving logic would be
440             larger, and stand out more clearly against the background boilerplate.
441              
442             =head1 SEE ALSO
443              
444             =over 4
445              
446             =item *
447              
448             L - manage flow of control in Continuation-Passing Style
449              
450             =back
451              
452             =head1 AUTHOR
453              
454             Paul Evans
455              
456             =cut
457              
458             0x55AA;