line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Sub::DeferredPartial; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
4
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
30615
|
use Sub::DeferredPartial::Attributes(); |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
36
|
|
6
|
2
|
|
|
2
|
|
988
|
use Sub::DeferredPartial::Op::Nullary(); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
37
|
|
7
|
2
|
|
|
2
|
|
1058
|
use Sub::DeferredPartial::Op::Unary(); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
45
|
|
8
|
2
|
|
|
2
|
|
1175
|
use Sub::DeferredPartial::Op::Binary(); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
44
|
|
9
|
2
|
|
|
2
|
|
11
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
140
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
use overload |
12
|
2
|
|
|
|
|
12
|
'&{}' => 'Subify' |
13
|
|
|
|
|
|
|
, '""' => 'Describe' |
14
|
|
|
|
|
|
|
, nomethod => 'NoMethod' |
15
|
2
|
|
|
2
|
|
3225
|
; |
|
2
|
|
|
|
|
2047
|
|
16
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
17
|
|
|
|
|
|
|
sub import |
18
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
19
|
|
|
|
|
|
|
{ |
20
|
2
|
|
|
2
|
|
16
|
my $class = shift; |
21
|
2
|
|
100
|
|
|
13
|
my $Name = shift || 'defer'; |
22
|
2
|
|
|
|
|
4
|
my $Caller = caller; |
23
|
|
|
|
|
|
|
|
24
|
2
|
|
|
|
|
4
|
*{"$Caller\::$Name"} = \&Defer; |
|
2
|
|
|
|
|
11
|
|
25
|
2
|
|
|
|
|
15
|
Sub::DeferredPartial::Attributes->import( $Caller ); |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
28
|
|
|
|
|
|
|
sub new |
29
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
30
|
|
|
|
|
|
|
{ |
31
|
1
|
|
|
1
|
0
|
3
|
my $class = shift; |
32
|
1
|
|
|
|
|
3
|
my $Sub = shift; |
33
|
1
|
|
|
|
|
2
|
my $Free = shift; |
34
|
1
|
|
50
|
|
|
12
|
my $Bound = shift || {}; |
35
|
|
|
|
|
|
|
|
36
|
1
|
|
|
|
|
10
|
bless { Sub => $Sub, F => $Free, B => $Bound } => $class; |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
39
|
|
|
|
|
|
|
sub Subify |
40
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
41
|
|
|
|
|
|
|
{ |
42
|
1
|
|
|
1
|
0
|
652
|
my $self = shift; |
43
|
|
|
|
|
|
|
|
44
|
1
|
50
|
|
1
|
|
8
|
return sub { return @_ ? $self->Apply( @_ ) : $self->Eval }; |
|
1
|
|
|
|
|
7
|
|
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
47
|
|
|
|
|
|
|
sub Apply |
48
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
49
|
|
|
|
|
|
|
{ |
50
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
51
|
1
|
|
|
|
|
6
|
my %Args = @_; |
52
|
1
|
|
|
|
|
2
|
my %F = %{$self->{F}}; |
|
1
|
|
|
|
|
4
|
|
53
|
1
|
|
|
|
|
2
|
my %B = %{$self->{B}}; |
|
1
|
|
|
|
|
3
|
|
54
|
|
|
|
|
|
|
|
55
|
1
|
|
|
|
|
7
|
while ( my ( $k, $v ) = each %Args ) |
56
|
|
|
|
|
|
|
{ |
57
|
1
|
50
|
|
|
|
4
|
confess "Bound parameter: $k" if exists $B{$k}; $B{$k} = $v; |
|
1
|
|
|
|
|
2
|
|
58
|
1
|
50
|
|
|
|
316
|
confess "Wrong parameter: $k" unless exists $F{$k}; delete $F{$k}; |
|
0
|
|
|
|
|
0
|
|
59
|
|
|
|
|
|
|
} |
60
|
0
|
|
|
|
|
0
|
return ref( $self )->new( $self->{Sub}, \%F, \%B ); |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
63
|
|
|
|
|
|
|
sub Eval |
64
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
65
|
|
|
|
|
|
|
{ |
66
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
67
|
|
|
|
|
|
|
|
68
|
0
|
|
|
|
|
0
|
confess "Free parameter: $_" for keys %{$self->{F}}; |
|
0
|
|
|
|
|
0
|
|
69
|
|
|
|
|
|
|
|
70
|
0
|
|
|
|
|
0
|
return $self->{Sub}->( %{$self->{B}} ); |
|
0
|
|
|
|
|
0
|
|
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
73
|
|
|
|
|
|
|
sub Free |
74
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
75
|
|
|
|
|
|
|
{ |
76
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
77
|
|
|
|
|
|
|
|
78
|
0
|
|
|
|
|
0
|
return $self->{F}; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
81
|
|
|
|
|
|
|
sub Describe |
82
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
83
|
|
|
|
|
|
|
{ |
84
|
1
|
|
|
1
|
0
|
86
|
my $self = shift; |
85
|
1
|
|
|
|
|
2
|
my @s; |
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
|
|
2
|
while ( my ( $k, $v ) = each %{$self->{B}} ) { push @s, "$k => $v"; } |
|
1
|
|
|
|
|
8
|
|
|
0
|
|
|
|
|
0
|
|
88
|
1
|
|
|
|
|
3
|
while ( my ( $k, $v ) = each %{$self->{F}} ) { push @s, "$k => ?" ; } |
|
1
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
89
|
|
|
|
|
|
|
|
90
|
1
|
|
|
|
|
7
|
return $self->{Sub} . ': ' . join ', ', @s; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
93
|
|
|
|
|
|
|
sub NoMethod |
94
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
95
|
|
|
|
|
|
|
{ |
96
|
0
|
|
|
0
|
0
|
0
|
my ( $Obj1, $Obj2, $Inv, $Op ) = @_; |
97
|
|
|
|
|
|
|
|
98
|
0
|
0
|
0
|
|
|
0
|
if ( defined $Obj2 || exists $Sub::DeferredPartial::Op::Binary::Ops{$Op} ) |
99
|
|
|
|
|
|
|
{ |
100
|
0
|
0
|
|
|
|
0
|
$Obj2 = Sub::DeferredPartial::Op::Nullary->new( $Obj2 ) unless ref $Obj2; |
101
|
0
|
0
|
|
|
|
0
|
( $Obj1, $Obj2 ) = ( $Obj2, $Obj1 ) if $Inv; |
102
|
0
|
|
|
|
|
0
|
return Sub::DeferredPartial::Op::Binary->new( $Op, $Obj1, $Obj2 ); |
103
|
|
|
|
|
|
|
} |
104
|
0
|
|
|
|
|
0
|
return Sub::DeferredPartial::Op::Unary->new( $Op, $Obj1 ); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
107
|
|
|
|
|
|
|
sub Defer |
108
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
109
|
|
|
|
|
|
|
{ |
110
|
1
|
|
|
1
|
0
|
12
|
my $Sub = shift; |
111
|
|
|
|
|
|
|
|
112
|
1
|
|
|
|
|
12
|
return __PACKAGE__->new( $Sub, Sub::DeferredPartial::Attributes->Hash( $Sub ) ); |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
115
|
|
|
|
|
|
|
1; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head1 NAME |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Sub::DeferredPartial - Deferred evaluation / partial application. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=head1 SYNOPSIS |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
use Sub::DeferredPartial 'def'; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
$S = def sub : P1 P2 P3 { %_=@_; join '', @_{qw(P1 P2 P3)} }; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
print $S->( P1 => 1, P2 => 2, P3 => 3 )->(); # 123 |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
$A = $S->( P3 => 1 ); # partial application |
130
|
|
|
|
|
|
|
$B = $S->( P3 => 2 ); |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
$C = $A + $B; # deferred evaluation |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
$D = $C->( P2 => 3 ); |
135
|
|
|
|
|
|
|
$E = $D->( P1 => 4 ); |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
print $E->(); # force evaluation: 863 |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
$F = $E - $D; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
$G = $F->( P1 => 0 ) / 2; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
print $G->(); # 400 |
144
|
|
|
|
|
|
|
print $G; # ( ( CODE(0x15e3818): P1 => 4, P2 => 3, P3 => 1 + CODE ... |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
$F->(); # Error: Free parameter : P1 |
147
|
|
|
|
|
|
|
$A->( P3 => 7 ); # Error: Bound parameter: P3 |
148
|
|
|
|
|
|
|
$A->( P4 => 7 ); # Error: Wrong parameter: P4 |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 DESCRIPTION |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
An instance of this class behaves like a sub (or, more precisely: subroutine |
153
|
|
|
|
|
|
|
reference), but it supports partial application and the evaluation of |
154
|
|
|
|
|
|
|
operators applied to such function objects is deferred too. |
155
|
|
|
|
|
|
|
That means, evaluation has to be forced explicitly (which makes it easier to |
156
|
|
|
|
|
|
|
add introspection capabilities). |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Objects that represent deferred (delayed, suspended) expressions are known |
159
|
|
|
|
|
|
|
as suspensions or thunks in various programming circles. |
160
|
|
|
|
|
|
|
Don't confuse with the same terms in the context of threads! |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
When you use this module, you can specify the name of a subroutine: |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
use Sub::DeferredPartial 'def'; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
or accept the default C<'defer'>: |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
use Sub::DeferredPartial; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
This subroutine will be imported into your current package and helps you to |
171
|
|
|
|
|
|
|
create an instance of C: |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
$S = defer sub : P1 P2 { "@_" }; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Please note that subroutine attributes are used to declare parameter names. |
176
|
|
|
|
|
|
|
Now, C<$S> is an instance of C: |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
print ref $S; # Sub::DeferredPartial |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
and knows about the subroutine reference and its parameters: |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
print $S; # CODE(0x15e3830): P1 => ?, P2 => ? |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Rudimentary introspection capabilities are available through stringification. |
185
|
|
|
|
|
|
|
The question marks indicate that all parameters are free (unbound). |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Parameters are passed as flattened hash to emulate named parameters: |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
$T = $S->( P1 => 1, P2 => 2 ); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
This time, a new suspensions is created where all parameters are bound: |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
print $T; # CODE(0x15e3830): P1 => 1, P2 => 2 |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Although all parameters are bound, the evaluation of the function is deferred |
196
|
|
|
|
|
|
|
and has to be forced explicitly: |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
print $T->(); # P1 1 P2 2 |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Up to this point, quite the same could be achieved with ordinary subroutines. |
201
|
|
|
|
|
|
|
Indeed, every time we define a function (i.e. create an abstraction), the |
202
|
|
|
|
|
|
|
evaluation of its body is deferred in some way. |
203
|
|
|
|
|
|
|
However, every application would force the evaluation of the body. |
204
|
|
|
|
|
|
|
And because Perl does not encourage currying, it would be tedious to write |
205
|
|
|
|
|
|
|
a closure returning function every time we need to support partial |
206
|
|
|
|
|
|
|
application. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
If you supply only some of the allowed arguments, a new suspension is |
209
|
|
|
|
|
|
|
created with a mix of free and bound parameters: |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
$A = $S->( P2 => 2 ); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
Parameter P1 is still free, whereas P2 is bound: |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
print $A; # CODE(0x15e3830): P2 => 2, P1 => ? |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
If you merely need currying, you may consider modules like |
218
|
|
|
|
|
|
|
L, |
219
|
|
|
|
|
|
|
L or |
220
|
|
|
|
|
|
|
L. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
However, this module goes further: The application of operators to |
223
|
|
|
|
|
|
|
suspensions: |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
$C = $A cmp $S->( P1 => 1 ); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
creates yet another (kind of) suspension: |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
print ref $C; # Sub::DeferredPartial::Op::Binary |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Depending on the operator - binary, unary or nullary (i.e. constants) - |
232
|
|
|
|
|
|
|
different subclasses are used. But that shouldn’t bother you too much. |
233
|
|
|
|
|
|
|
Assignment operators (mutators) are not supported. |
234
|
|
|
|
|
|
|
Our poor man's reflection yields: |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
print $C; # ( CODE(...): P2 => 2, P1 => ? cmp CODE(...): P1 => 1, P2 => ? ) |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
A suspended binary operator expects the union of the free parameters of |
239
|
|
|
|
|
|
|
its operands: |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
print map $C->( P1 => 1 )->( P2 => $_ )->(), 1..3; # 10-1 |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
The deferred evaluation strategy allows to write down expressions in |
244
|
|
|
|
|
|
|
a natural way - without the need for a wrapper function. |
245
|
|
|
|
|
|
|
This is the chief difference to the C<*::Curry> modules mentioned above. |
246
|
|
|
|
|
|
|
Partial application aside, what comes closest is the |
247
|
|
|
|
|
|
|
L |
248
|
|
|
|
|
|
|
in the C module. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=over 1 |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item Free parameter ... |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
$A->(); # Free parameter: P1 |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
You cannot force evaluation until all parameters are bound. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=item Bound parameter ... |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
$A->( P2 => 7 ); # Bound parameter: P2 |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
You cannot bind a parameter that is already bound. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=item Wrong parameter ... |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
$A->( P3 => 7 ); # Wrong parameter: P3 |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
You cannot bind a parameter that was not declared. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=back
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=head1 TODO |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
=over
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=item Lazy evaluation
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
Memoization is a common optimization strategy in this context. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=item Conditional operator
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
An I or I expression may be useful. |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=item Introspection capabilities
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
Current introspection capabilities (stringification) are quite inflexible |
289
|
|
|
|
|
|
|
and poking into the internals isn't state of the art ... |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=back
|
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head1 ACKNOWLEDGMENT |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Many thanks to Gottlob Frege, Moses SchEnfinkel and Haskell Curry |
296
|
|
|
|
|
|
|
for laying the groundwork. |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head1 AUTHOR |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
Steffen Goeldner |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=head1 COPYRIGHT |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Copyright (c) 2004 Steffen Goeldner. All rights reserved. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or |
307
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=head1 SEE ALSO |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
L, L, L, L, L. |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=cut |