File Coverage

lib/Perl6/GatherTake.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Perl6::GatherTake;
2             our $VERSION = '0.0.3';
3              
4             =head1 NAME
5              
6             Perl6::GatherTake - Perl 6 like C for Perl 5
7              
8             =head1 SYNOPSIS
9              
10             use Perl6::GatherTake;
11              
12             my $powers_of_two = gather {
13             my $i = 1;
14             for (;;) {
15             take $i;
16             $i *= 2;
17             }
18             };
19              
20             print $powers_of_two->[3], "\n";
21             # output: 8
22              
23             =head1 DESCRIPTION
24              
25             Perl6::GatherTake implements an API for producing partial computation results
26             on the fly, storing them in a lazy list.
27              
28             A word of warning: This module tries to explore some language concepts. It is
29             B.
30              
31             A C block returns a reference to a (tied) array. Each call
32             to C inside the block pushes its arguments to that array. The block
33             is only run as needed to produce results (but see "BUGS AND LIMITATIONS"
34             below), which means that you can put infinite loops inside the C
35             block as long as it calls C on a regular basis.
36              
37             Instead of this common construct:
38              
39             my @results;
40             for (@data){
41             # computations here
42             if ($result =~ m/super regex/){
43             push @results, $result;
44             }
45             }
46              
47             You can now write
48              
49             my $results = gather {
50             for (@data){
51             # computations here
52             if ($result =~ m/super regex/){
53             take $result;
54             }
55             }
56             };
57              
58             It has the nice side effect that the computations are only executed as the
59             array elements are accessed, so if the end of the array is never used you
60             can save much time here.
61              
62             Nested C blocks are supported, a C always supplies
63             data to the innermost C block.
64              
65             Note that if a C block is an infinite loop, you're responsible for
66             not accessing all elements. If you do something stupid like iterating over
67             all items, joining them or copying the array (C)
68             you have an infinite loop (until you run out of memory).
69              
70             Assigning to an array element triggers evaluation until the index of the
71             changed item is reached.
72              
73             =head1 BUGS AND LIMITATIONS
74              
75             This is a prototype module and is neither stable nor well-tested at the
76             moment.
77              
78             =over 2
79              
80             =item *
81              
82             Due to the L based implementation (and the author's missing
83             understanding of L's concepts) the lazyness is limited:
84             C-blocks might be run up to the first occurance of C before
85             a element is fetched from the associated array.
86              
87             =item *
88              
89             C doesn't return "the right" value for an array
90             reference that is returend by a gather-take block. More precisely it returns
91             the number of already computed values plus one (unless the gather block is
92             exhausted). This means that iterating over C will result in an
93             undefined element at the end if the block returns only a finite number of
94             elements.
95              
96             =item *
97              
98             This module consumes much more resources than desirable: for each
99             gather-take-block it (currently) maintains a tied array (which is implemented
100             as a blessed hash) which holds all the computed values so far, a C and
101             a C object.
102              
103             =item *
104              
105             C doesn't default to C<$_>.
106              
107             =item *
108              
109             More advanced array operations (like slices, C etc.) aren't tested yet.
110              
111             =back
112              
113             =head1 LICENSE
114              
115             This package is free software, you can use it under the same terms as Perl
116             itself.
117              
118             All example and test code in this distribution is "Public Domain" (*), i.e.
119             you may use it in any way you want.
120              
121             (*) German copyright laws always grant the original author some rights, so
122             I can't really place things in the "Public Domain". But don't let that bother
123             you.
124              
125             =head1 AUTHOR
126              
127             Moritz Lenz, L, L.
128             E-Mail Emoritz@faui2k3.orgE.
129              
130             =head1 DEVELOPMENT
131              
132             You can obtain the latest development version via subversion:
133              
134             svn co https://faui2k3.org/svn/moritz/cpan/Perl6-GatherTake
135              
136             Patches and comments are welcome.
137              
138             =cut
139              
140 6     6   175579 use strict;
  6         16  
  6         236  
141 6     6   31 use warnings;
  6         14  
  6         162  
142              
143 6     6   7154 use Data::Dumper;
  6         68255  
  6         447  
144 6     6   50 use base 'Exporter';
  6         11  
  6         656  
145 6     6   2794 use Perl6::GatherTake::LazyList;
  0            
  0            
146             use Coro;
147             use Coro::Channel;
148             use Carp qw(confess);
149             use Scalar::Util qw(refaddr);
150             our @EXPORT = qw(gather take);
151              
152             our %_coro_to_queue;
153              
154             sub gather(&@) {
155             my $code = shift;
156             # cheat prototype by prepending '&' to method call:
157             my $coro = &async($code, @_);
158             my @result = ();
159             my $queue = Coro::Channel->new(1);
160             # print "Initialized coro $coro\n";
161             $_coro_to_queue{refaddr($coro)} = $queue;
162             tie @result, 'Perl6::GatherTake::LazyList', $coro, $queue;
163             return \@result;
164             }
165              
166             sub take {
167             my $c = Coro::current;
168             # print "Take: $c\n";
169             for (@_){
170             $_coro_to_queue{refaddr($c)}->put($_);
171             }
172             }
173              
174             1;