| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
23
|
|
|
23
|
|
6111765
|
use 5.008001; |
|
|
23
|
|
|
|
|
100
|
|
|
2
|
23
|
|
|
23
|
|
146
|
use strict; |
|
|
23
|
|
|
|
|
44
|
|
|
|
23
|
|
|
|
|
804
|
|
|
3
|
23
|
|
|
23
|
|
147
|
use warnings; |
|
|
23
|
|
|
|
|
77
|
|
|
|
23
|
|
|
|
|
2493
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Sub::MultiMethod; |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:TOBYINK'; |
|
8
|
|
|
|
|
|
|
our $VERSION = '1.003'; |
|
9
|
|
|
|
|
|
|
|
|
10
|
23
|
|
|
23
|
|
153
|
use B (); |
|
|
23
|
|
|
|
|
75
|
|
|
|
23
|
|
|
|
|
664
|
|
|
11
|
23
|
|
|
23
|
|
5330
|
use Eval::TypeTiny qw( set_subname ); |
|
|
23
|
|
|
|
|
33696
|
|
|
|
23
|
|
|
|
|
173
|
|
|
12
|
23
|
|
|
|
|
176
|
use Exporter::Shiny qw( |
|
13
|
|
|
|
|
|
|
multimethod monomethod |
|
14
|
|
|
|
|
|
|
multifunction monofunction |
|
15
|
|
|
|
|
|
|
VOID SCALAR LIST NONVOID NONSCALAR NONLIST |
|
16
|
23
|
|
|
23
|
|
97681
|
); |
|
|
23
|
|
|
|
|
13502
|
|
|
17
|
23
|
|
|
23
|
|
13953
|
use Role::Hooks; |
|
|
23
|
|
|
|
|
184357
|
|
|
|
23
|
|
|
|
|
1517
|
|
|
18
|
23
|
|
|
23
|
|
298
|
use List::Util qw( max min any ); |
|
|
23
|
|
|
|
|
105
|
|
|
|
23
|
|
|
|
|
2568
|
|
|
19
|
23
|
|
|
23
|
|
191
|
use Scalar::Util qw( refaddr ); |
|
|
23
|
|
|
|
|
54
|
|
|
|
23
|
|
|
|
|
1227
|
|
|
20
|
23
|
|
|
23
|
|
17934
|
use Type::Params (); |
|
|
23
|
|
|
|
|
1472571
|
|
|
|
23
|
|
|
|
|
1085
|
|
|
21
|
23
|
|
|
23
|
|
213
|
use Types::TypeTiny qw( TypeTiny ); |
|
|
23
|
|
|
|
|
48
|
|
|
|
23
|
|
|
|
|
226
|
|
|
22
|
23
|
|
|
23
|
|
90133
|
use Types::Standard qw( -types -is ); |
|
|
23
|
|
|
|
|
56
|
|
|
|
23
|
|
|
|
|
233
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use constant { |
|
25
|
23
|
|
|
|
|
20183
|
VOID => 'VOID', |
|
26
|
|
|
|
|
|
|
LIST => 'LIST', |
|
27
|
|
|
|
|
|
|
SCALAR => 'SCALAR', |
|
28
|
|
|
|
|
|
|
NONVOID => '~VOID', |
|
29
|
|
|
|
|
|
|
NONLIST => '~LIST', |
|
30
|
|
|
|
|
|
|
NONSCALAR => '~SCALAR', |
|
31
|
23
|
|
|
23
|
|
427165
|
}; |
|
|
23
|
|
|
|
|
55
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Options other than these will be passed through to |
|
34
|
|
|
|
|
|
|
# Type::Params. |
|
35
|
|
|
|
|
|
|
# |
|
36
|
|
|
|
|
|
|
my %KNOWN_OPTIONS = ( |
|
37
|
|
|
|
|
|
|
alias => 1, |
|
38
|
|
|
|
|
|
|
code => 1, |
|
39
|
|
|
|
|
|
|
compiled => 1, |
|
40
|
|
|
|
|
|
|
copied => 1, |
|
41
|
|
|
|
|
|
|
declaration_order => 1, |
|
42
|
|
|
|
|
|
|
die => 1, |
|
43
|
|
|
|
|
|
|
height => 1, |
|
44
|
|
|
|
|
|
|
if => 1, |
|
45
|
|
|
|
|
|
|
is_monomethod => 1, |
|
46
|
|
|
|
|
|
|
method => 1, |
|
47
|
|
|
|
|
|
|
named => 'legacy', |
|
48
|
|
|
|
|
|
|
no_dispatcher => 1, |
|
49
|
|
|
|
|
|
|
return => 1, |
|
50
|
|
|
|
|
|
|
score => 1, |
|
51
|
|
|
|
|
|
|
signature => 'legacy', |
|
52
|
|
|
|
|
|
|
want => 1, |
|
53
|
|
|
|
|
|
|
); |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# But not these! |
|
56
|
|
|
|
|
|
|
# |
|
57
|
|
|
|
|
|
|
my %BAD_OPTIONS = ( |
|
58
|
|
|
|
|
|
|
want_details => 1, |
|
59
|
|
|
|
|
|
|
want_object => 1, |
|
60
|
|
|
|
|
|
|
want_source => 1, |
|
61
|
|
|
|
|
|
|
goto_next => 1, |
|
62
|
|
|
|
|
|
|
on_die => 1, |
|
63
|
|
|
|
|
|
|
message => 1, |
|
64
|
|
|
|
|
|
|
); |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my %CACHE = (); |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
{ |
|
69
|
|
|
|
|
|
|
my %CANDIDATES; |
|
70
|
|
|
|
|
|
|
sub _get_multimethods_ref { |
|
71
|
450
|
|
|
450
|
|
934
|
my ($me, $target) = @_; |
|
72
|
450
|
100
|
|
|
|
1242
|
if ( not $CANDIDATES{$target} ) { |
|
73
|
68
|
|
|
|
|
261
|
$CANDIDATES{$target} = {}; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
450
|
|
|
|
|
1106
|
$CANDIDATES{$target}; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub get_multimethods { |
|
80
|
34
|
|
|
34
|
1
|
77
|
my ($me, $target) = @_; |
|
81
|
34
|
|
|
|
|
54
|
sort keys %{ $me->_get_multimethods_ref($target) }; |
|
|
34
|
|
|
|
|
98
|
|
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub _get_multimethod_candidates_ref { |
|
85
|
410
|
|
|
410
|
|
883
|
my ($me, $target, $method_name) = @_; |
|
86
|
410
|
100
|
|
|
|
1091
|
my ( $package_key, $method_key ) = ref( $method_name ) |
|
87
|
|
|
|
|
|
|
? ( '__CODE__', refaddr( $method_name ) ) |
|
88
|
|
|
|
|
|
|
: ( $target, $method_name ); |
|
89
|
410
|
|
|
|
|
1128
|
my $mm = $me->_get_multimethods_ref( $package_key ); |
|
90
|
410
|
|
100
|
|
|
1962
|
$mm->{$method_key} ||= []; |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub _clear_multimethod_candidates_ref { |
|
94
|
4
|
|
|
4
|
|
12
|
my ( $me, $target, $method_name ) = ( shift, @_ ); |
|
95
|
4
|
|
|
|
|
17
|
$me->clear_cache; |
|
96
|
4
|
50
|
|
|
|
15
|
my ( $package_key, $method_key ) = ref( $method_name ) |
|
97
|
|
|
|
|
|
|
? ( '__CODE__', refaddr( $method_name ) ) |
|
98
|
|
|
|
|
|
|
: ( $target, $method_name ); |
|
99
|
4
|
|
|
|
|
13
|
my $mm = $me->_get_multimethods_ref( $package_key ); |
|
100
|
4
|
|
|
|
|
10
|
delete $mm->{$method_key}; |
|
101
|
4
|
|
|
|
|
11
|
return $me; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub get_multimethod_candidates { |
|
105
|
90
|
|
|
90
|
1
|
258
|
my ($me, $target, $method_name) = @_; |
|
106
|
90
|
|
|
|
|
156
|
@{ $me->_get_multimethod_candidates_ref($target, $method_name) }; |
|
|
90
|
|
|
|
|
245
|
|
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub has_multimethod_candidates { |
|
110
|
94
|
|
|
94
|
1
|
308
|
my ($me, $target, $method_name) = @_; |
|
111
|
94
|
|
|
|
|
210
|
scalar @{ $me->_get_multimethod_candidates_ref($target, $method_name) }; |
|
|
94
|
|
|
|
|
257
|
|
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub _add_multimethod_candidate { |
|
115
|
226
|
|
|
226
|
|
743
|
my ($me, $target, $method_name, $spec) = @_; |
|
116
|
226
|
|
|
|
|
909
|
$me->clear_cache; |
|
117
|
226
|
|
|
|
|
790
|
my $mmc = $me->_get_multimethod_candidates_ref($target, $method_name); |
|
118
|
23
|
|
|
23
|
|
213
|
no warnings 'uninitialized'; |
|
|
23
|
|
|
|
|
58
|
|
|
|
23
|
|
|
|
|
9660
|
|
|
119
|
226
|
50
|
66
|
|
|
1059
|
if ( @$mmc and $spec->{method} != $mmc->[0]{method} ) { |
|
120
|
0
|
|
|
|
|
0
|
require Carp; |
|
121
|
|
|
|
|
|
|
Carp::carp(sprintf( |
|
122
|
|
|
|
|
|
|
"Added multimethod candidate for %s with method=>%d but expected method=>%d", |
|
123
|
|
|
|
|
|
|
$method_name, |
|
124
|
|
|
|
|
|
|
$spec->{method}, |
|
125
|
|
|
|
|
|
|
$mmc->[0]{method}, |
|
126
|
0
|
|
|
|
|
0
|
)); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
226
|
|
|
|
|
577
|
push @$mmc, $spec; |
|
129
|
226
|
|
|
|
|
592
|
$me; |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub clear_cache { |
|
133
|
230
|
|
|
230
|
1
|
582
|
%CACHE = (); |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub get_cache { |
|
137
|
0
|
|
|
0
|
1
|
0
|
return \%CACHE; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub get_all_multimethod_candidates { |
|
141
|
38
|
|
|
38
|
1
|
173
|
my ($me, $target, $method_name, $is_method) = @_; |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Figure out which packages to consider when finding candidates. |
|
144
|
38
|
|
|
|
|
85
|
my (@packages, $is_coderef_method); |
|
145
|
38
|
100
|
66
|
|
|
1186
|
if (is_Int $method_name or is_ScalarRef $method_name) { |
|
146
|
2
|
|
|
|
|
6
|
@packages = '__CODE__'; |
|
147
|
2
|
|
|
|
|
4
|
$is_coderef_method = 1; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
else { |
|
150
|
|
|
|
|
|
|
@packages = $is_method |
|
151
|
36
|
100
|
|
|
|
126
|
? @{ mro::get_linear_isa($target) } |
|
|
32
|
|
|
|
|
256
|
|
|
152
|
|
|
|
|
|
|
: $target; |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
38
|
|
|
|
|
94
|
my $curr_height = @packages; |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Find candidates from each package |
|
158
|
38
|
|
|
|
|
77
|
my @candidates; |
|
159
|
38
|
|
|
|
|
89
|
my $final_fallback = undef; |
|
160
|
38
|
|
|
|
|
136
|
PACKAGE: while (@packages) { |
|
161
|
94
|
|
|
|
|
213
|
my $p = shift @packages; |
|
162
|
94
|
|
|
|
|
185
|
my @c; |
|
163
|
94
|
|
|
|
|
328
|
my $found = $me->has_multimethod_candidates($p, $method_name); |
|
164
|
94
|
100
|
|
|
|
337
|
if ($found) { |
|
|
|
50
|
|
|
|
|
|
|
165
|
56
|
|
|
|
|
194
|
@c = $me->get_multimethod_candidates($p, $method_name); |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
elsif (not $is_coderef_method) { |
|
168
|
23
|
|
|
23
|
|
342
|
no strict 'refs'; |
|
|
23
|
|
|
|
|
219
|
|
|
|
23
|
|
|
|
|
79496
|
|
|
169
|
38
|
100
|
|
|
|
80
|
if (exists &{"$p\::$method_name"}) { |
|
|
38
|
|
|
|
|
260
|
|
|
170
|
|
|
|
|
|
|
# We found a potential monomethod. |
|
171
|
4
|
|
|
|
|
9
|
my $coderef = \&{"$p\::$method_name"}; |
|
|
4
|
|
|
|
|
15
|
|
|
172
|
4
|
50
|
|
|
|
18
|
if (!$me->known_dispatcher($coderef)) { |
|
173
|
|
|
|
|
|
|
# Definite monomethod. Stop falling back. |
|
174
|
4
|
|
|
|
|
9
|
$final_fallback = $coderef; |
|
175
|
4
|
|
|
|
|
13
|
last PACKAGE; |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
} |
|
178
|
34
|
|
|
|
|
80
|
@c = (); |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
# Record their height in case we need it later |
|
181
|
90
|
|
|
|
|
451
|
$_->{height} = $curr_height for @c; |
|
182
|
90
|
|
|
|
|
202
|
push @candidates, @c; |
|
183
|
90
|
|
|
|
|
253
|
--$curr_height; |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# If a monomethod was found, use it as last resort |
|
187
|
38
|
100
|
|
|
|
128
|
if (defined $final_fallback) { |
|
188
|
|
|
|
|
|
|
push @candidates, { |
|
189
|
16
|
|
|
16
|
|
67
|
signature => sub { @_ }, |
|
190
|
4
|
|
|
|
|
29
|
code => $final_fallback, |
|
191
|
|
|
|
|
|
|
}; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
38
|
|
|
|
|
434
|
return @candidates; |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
{ |
|
198
|
|
|
|
|
|
|
my %DISPATCHERS; |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub known_dispatcher { |
|
201
|
52
|
|
|
52
|
1
|
139
|
my ($me, $coderef) = @_; |
|
202
|
52
|
|
|
|
|
292
|
$DISPATCHERS{refaddr($coderef)}; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub _mark_as_dispatcher { |
|
206
|
48
|
|
|
48
|
|
141
|
my ($me, $coderef) = @_; |
|
207
|
48
|
|
|
|
|
186
|
$DISPATCHERS{refaddr($coderef)} = 1; |
|
208
|
48
|
|
|
|
|
92
|
$me; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _unmark_as_dispatcher { |
|
212
|
0
|
|
|
0
|
|
0
|
my ($me, $coderef) = @_; |
|
213
|
0
|
|
|
|
|
0
|
$DISPATCHERS{refaddr($coderef)} = 0; |
|
214
|
0
|
|
|
|
|
0
|
$me; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub _generate_exported_function { |
|
219
|
80
|
|
|
80
|
|
347
|
my ( $me, $name, $args, $globals ) = ( shift, @_ ); |
|
220
|
|
|
|
|
|
|
|
|
221
|
80
|
|
|
|
|
242
|
my $target = $globals->{into}; |
|
222
|
80
|
50
|
33
|
|
|
610
|
if ( ref $target or not defined $target ) { |
|
223
|
0
|
|
|
|
|
0
|
require Carp; |
|
224
|
0
|
|
|
|
|
0
|
Carp::croak( "Function $name can only be installed into a package by package name" ); |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
80
|
50
|
|
|
|
178
|
my %defaults = %{ $args->{defaults} || {} }; |
|
|
80
|
|
|
|
|
501
|
|
|
228
|
80
|
|
100
|
|
|
436
|
my $api_call = $args->{api_call} || 'install_candidate'; |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
return sub { |
|
231
|
106
|
|
|
106
|
|
5708939
|
my $sub_name = shift; |
|
232
|
|
|
|
|
|
|
|
|
233
|
106
|
|
|
|
|
233
|
my @tmp_sigs; |
|
234
|
106
|
|
33
|
|
|
1081
|
while ( is_ArrayRef $_[0] or is_HashRef $_[0] ) { |
|
235
|
0
|
|
|
|
|
0
|
push @tmp_sigs, shift; |
|
236
|
|
|
|
|
|
|
} |
|
237
|
|
|
|
|
|
|
|
|
238
|
106
|
|
|
|
|
232
|
my @tmp_code; |
|
239
|
106
|
50
|
|
|
|
440
|
if ( @_ % 2 == 1 ) { |
|
240
|
0
|
0
|
|
|
|
0
|
if ( is_CodeRef $_[-1] ) { |
|
241
|
0
|
|
|
|
|
0
|
push @tmp_code, pop; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
else { |
|
244
|
0
|
|
|
|
|
0
|
require Carp; |
|
245
|
0
|
|
|
|
|
0
|
Carp::croak( "Odd-length list passed to $name; should be key-value pairs" ); |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
|
|
249
|
106
|
|
|
|
|
597
|
my %spec = @_; |
|
250
|
106
|
50
|
0
|
|
|
399
|
$spec{positional} ||= delete $spec{pos} if exists $spec{pos}; |
|
251
|
106
|
50
|
0
|
|
|
347
|
$spec{multiple} ||= delete $spec{multi} if exists $spec{multi}; |
|
252
|
|
|
|
|
|
|
|
|
253
|
106
|
|
|
|
|
425
|
while ( my $sig = shift @tmp_sigs ) { |
|
254
|
0
|
0
|
|
|
|
0
|
if ( is_ArrayRef $sig ) { |
|
|
|
0
|
|
|
|
|
|
|
255
|
0
|
0
|
|
|
|
0
|
if ( $spec{positional} ) { |
|
256
|
0
|
|
|
|
|
0
|
require Carp; |
|
257
|
0
|
|
|
|
|
0
|
Carp::croak( "Leading arrayref passed as argument to $name; unexpected positional signature found in list of key-value pairs" ); |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
else { |
|
260
|
0
|
|
|
|
|
0
|
$spec{positional} = $sig; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
} |
|
263
|
|
|
|
|
|
|
elsif ( is_HashRef $sig ) { |
|
264
|
0
|
0
|
|
|
|
0
|
if ( $spec{named} ) { |
|
265
|
0
|
|
|
|
|
0
|
require Carp; |
|
266
|
0
|
|
|
|
|
0
|
Carp::croak( "Leading hashref passed as argument to $name; unexpected named signature found in list of key-value pairs" ); |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
else { |
|
269
|
0
|
|
|
|
|
0
|
$spec{named} = [ %$sig ]; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
106
|
50
|
66
|
|
|
366
|
if ( $spec{named} and $spec{positional} ) { |
|
275
|
0
|
|
0
|
|
|
0
|
push @{ $spec{multiple} ||= [] }, ( |
|
276
|
|
|
|
|
|
|
{ named => delete $spec{named}, named_to_list => !!1 }, |
|
277
|
|
|
|
|
|
|
{ positional => delete $spec{positional} }, |
|
278
|
0
|
|
|
|
|
0
|
); |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
106
|
50
|
|
|
|
293
|
if ( @tmp_code ) { |
|
282
|
0
|
0
|
|
|
|
0
|
if ( $spec{code} ) { |
|
283
|
0
|
|
|
|
|
0
|
require Carp; |
|
284
|
0
|
|
|
|
|
0
|
Carp::croak( "Trailing coderef passed as argument to $name; unexpected code key found in list of key-value pairs" ); |
|
285
|
|
|
|
|
|
|
} |
|
286
|
0
|
|
|
|
|
0
|
$spec{code} = shift @tmp_code; |
|
287
|
|
|
|
|
|
|
} |
|
288
|
|
|
|
|
|
|
|
|
289
|
106
|
0
|
66
|
|
|
317
|
unless ( exists $spec{code} or exists $spec{return} or exists $spec{die} ) { |
|
|
|
|
33
|
|
|
|
|
|
290
|
0
|
|
|
|
|
0
|
require Carp; |
|
291
|
0
|
|
|
|
|
0
|
Carp::croak( "Missing 'code', 'return', or 'die'" ); |
|
292
|
|
|
|
|
|
|
} |
|
293
|
|
|
|
|
|
|
|
|
294
|
106
|
50
|
|
|
|
338
|
if ( ref $spec{return} ) { |
|
295
|
0
|
|
|
|
|
0
|
require Carp; |
|
296
|
0
|
|
|
|
|
0
|
Carp::croak( "Setting 'return' to a reference is not supported" ); |
|
297
|
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
|
|
299
|
106
|
100
|
|
|
|
422
|
if ( $defaults{no_dispatcher} eq 'auto' ) { |
|
300
|
46
|
|
|
|
|
468
|
$defaults{no_dispatcher} = 0+!! 'Role::Hooks'->is_role( $target ); |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
|
|
303
|
106
|
100
|
|
|
|
3118
|
$me->$api_call( |
|
304
|
|
|
|
|
|
|
$target, |
|
305
|
|
|
|
|
|
|
$sub_name, |
|
306
|
|
|
|
|
|
|
%defaults, |
|
307
|
|
|
|
|
|
|
'package' => $target, |
|
308
|
|
|
|
|
|
|
'subname' => ( ref($sub_name) ? '__ANON__' : $sub_name ), |
|
309
|
|
|
|
|
|
|
%spec, |
|
310
|
|
|
|
|
|
|
); |
|
311
|
80
|
|
|
|
|
1150
|
}; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub _generate_multimethod { |
|
315
|
46
|
|
|
46
|
|
281082
|
my ( $me, $name, $args, $globals ) = ( shift, @_ ); |
|
316
|
46
|
50
|
|
|
|
415
|
$args->{defaults}{no_dispatcher} = 'auto' unless exists $args->{defaults}{no_dispatcher}; |
|
317
|
46
|
50
|
|
|
|
271
|
$args->{defaults}{method} = !!1 unless exists $args->{defaults}{method}; |
|
318
|
46
|
|
|
|
|
416
|
return $me->_generate_exported_function( $name, $args, $globals ); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub _generate_monomethod { |
|
322
|
12
|
|
|
12
|
|
1995
|
my ( $me, $name, $args, $globals ) = ( shift, @_ ); |
|
323
|
12
|
50
|
|
|
|
86
|
$args->{defaults}{no_dispatcher} = !!1 unless exists $args->{defaults}{no_dispatcher}; |
|
324
|
12
|
50
|
|
|
|
59
|
$args->{defaults}{method} = !!1 unless exists $args->{defaults}{method}; |
|
325
|
12
|
|
|
|
|
69
|
$args->{api_call} = 'install_monomethod'; |
|
326
|
12
|
|
|
|
|
49
|
return $me->_generate_exported_function( $name, $args, $globals ); |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub _generate_multifunction { |
|
330
|
12
|
|
|
12
|
|
1876
|
my ( $me, $name, $args, $globals ) = ( shift, @_ ); |
|
331
|
12
|
50
|
|
|
|
149
|
$args->{defaults}{no_dispatcher} = 'auto' unless exists $args->{defaults}{no_dispatcher}; |
|
332
|
12
|
50
|
|
|
|
54
|
$args->{defaults}{method} = !!0 unless exists $args->{defaults}{method}; |
|
333
|
12
|
|
|
|
|
69
|
return $me->_generate_exported_function( $name, $args, $globals ); |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub _generate_monofunction { |
|
337
|
10
|
|
|
10
|
|
953
|
my ( $me, $name, $args, $globals ) = ( shift, @_ ); |
|
338
|
10
|
50
|
|
|
|
82
|
$args->{defaults}{no_dispatcher} = !!1 unless exists $args->{defaults}{no_dispatcher}; |
|
339
|
10
|
50
|
|
|
|
67
|
$args->{defaults}{method} = !!0 unless exists $args->{defaults}{method}; |
|
340
|
10
|
|
|
|
|
25
|
$args->{api_call} = 'install_monomethod'; |
|
341
|
10
|
|
|
|
|
33
|
return $me->_generate_exported_function( $name, $args, $globals ); |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
sub _extract_type_params_spec { |
|
345
|
106
|
|
|
106
|
|
336
|
my ( $me, $target, $sub_name, $spec ) = ( shift, @_ ); |
|
346
|
|
|
|
|
|
|
|
|
347
|
106
|
|
|
|
|
336
|
my %tp = ( method => 1 ); |
|
348
|
106
|
50
|
|
|
|
486
|
$tp{method} = $spec->{method} if defined $spec->{method}; |
|
349
|
|
|
|
|
|
|
|
|
350
|
106
|
100
|
|
|
|
561
|
if ( is_ArrayRef $spec->{signature} ) { |
|
351
|
51
|
100
|
|
|
|
158
|
my $key = $spec->{named} ? 'named' : 'positional'; |
|
352
|
51
|
|
|
|
|
167
|
$tp{$key} = delete $spec->{signature}; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
else { |
|
355
|
55
|
100
|
|
|
|
167
|
$tp{named} = $spec->{named} if ref $spec->{named}; |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# Options which are not known by this module must be intended for |
|
359
|
|
|
|
|
|
|
# Type::Params instead. |
|
360
|
106
|
|
|
|
|
504
|
for my $key ( keys %$spec ) { |
|
361
|
|
|
|
|
|
|
|
|
362
|
614
|
100
|
100
|
|
|
2353
|
next if ( $KNOWN_OPTIONS{$key} or $key =~ /^_/ ); |
|
363
|
|
|
|
|
|
|
|
|
364
|
264
|
50
|
|
|
|
650
|
if ( $BAD_OPTIONS{$key} ) { |
|
365
|
0
|
|
|
|
|
0
|
require Carp; |
|
366
|
0
|
|
|
|
|
0
|
Carp::carp( "Unsupported option: $key" ); |
|
367
|
0
|
|
|
|
|
0
|
next; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
264
|
|
|
|
|
819
|
$tp{$key} = delete $spec->{$key}; |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
|
|
373
|
106
|
|
33
|
|
|
468
|
$tp{package} ||= $target; |
|
374
|
106
|
50
|
66
|
|
|
325
|
$tp{subname} ||= ref( $sub_name ) ? '__ANON__' : $sub_name; |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# Historically we allowed method=2, etc |
|
377
|
106
|
100
|
|
|
|
515
|
if ( is_Int $tp{method} ) { |
|
378
|
98
|
100
|
|
|
|
322
|
if ( $tp{method} > 1 ) { |
|
379
|
4
|
|
|
|
|
11
|
my $excess = $tp{method} - 1; |
|
380
|
4
|
|
|
|
|
10
|
$tp{method} = 1; |
|
381
|
4
|
50
|
|
|
|
19
|
ref( $tp{head} ) ? push( @{ $tp{head} }, ( Any ) x $excess ) : ( $tp{head} += $excess ); |
|
|
0
|
|
|
|
|
0
|
|
|
382
|
|
|
|
|
|
|
} |
|
383
|
98
|
100
|
|
|
|
391
|
if ( $tp{method} == 1 ) { |
|
384
|
86
|
|
|
|
|
465
|
$tp{method} = Any; |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
} |
|
387
|
|
|
|
|
|
|
|
|
388
|
106
|
50
|
66
|
|
|
5191
|
if ( not ( $tp{named} or $tp{pos} or $tp{positional} or $tp{multi} or $tp{multiple} ) ) { |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
389
|
4
|
|
|
|
|
19
|
$tp{pos} = [ Slurpy[Any] ]; |
|
390
|
4
|
|
|
|
|
2227
|
$spec->{smiple} = 1; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
|
|
393
|
106
|
|
|
|
|
430
|
$spec->{signature_spec} = \%tp; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
my %delete_while_copying = ( |
|
397
|
|
|
|
|
|
|
_id => '_id should be unique', |
|
398
|
|
|
|
|
|
|
alias => 'alias should only be installed into package where originally declared', |
|
399
|
|
|
|
|
|
|
copied => 'this will be set after copying', |
|
400
|
|
|
|
|
|
|
height => 'this should never be kept anyway', |
|
401
|
|
|
|
|
|
|
is_monomethod => 'if it has been copied, it is no longer mono!', |
|
402
|
|
|
|
|
|
|
no_dispatcher => 'after a candidate gets copied from a role to a class, there SHOULD be a dispatcher', |
|
403
|
|
|
|
|
|
|
); |
|
404
|
|
|
|
|
|
|
sub copy_package_candidates { |
|
405
|
20
|
|
|
20
|
1
|
43
|
my $me = shift; |
|
406
|
20
|
|
|
|
|
55
|
my (@sources) = @_; |
|
407
|
20
|
|
|
|
|
40
|
my $target = pop @sources; |
|
408
|
|
|
|
|
|
|
|
|
409
|
20
|
|
|
|
|
48
|
for my $source (@sources) { |
|
410
|
20
|
|
|
|
|
96
|
for my $method_name ($me->get_multimethods($source)) { |
|
411
|
20
|
|
|
|
|
63
|
for my $candidate ($me->get_multimethod_candidates($source, $method_name)) { |
|
412
|
|
|
|
|
|
|
my %new = map { |
|
413
|
128
|
|
|
|
|
440
|
$delete_while_copying{$_} |
|
414
|
|
|
|
|
|
|
? () |
|
415
|
642
|
100
|
|
|
|
1941
|
: ( $_ => $candidate->{$_} ) |
|
416
|
|
|
|
|
|
|
} keys %$candidate; |
|
417
|
128
|
|
|
|
|
353
|
$new{copied} = 1; |
|
418
|
128
|
|
|
|
|
346
|
$me->_add_multimethod_candidate($target, $method_name, \%new); |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
sub install_missing_dispatchers { |
|
425
|
14
|
|
|
14
|
1
|
611
|
my $me = shift; |
|
426
|
14
|
|
|
|
|
33
|
my ($target) = @_; |
|
427
|
|
|
|
|
|
|
|
|
428
|
14
|
|
|
|
|
48
|
for my $method_name ($me->get_multimethods($target)) { |
|
429
|
14
|
|
|
|
|
46
|
my ($first) = $me->get_multimethod_candidates($target, $method_name); |
|
430
|
|
|
|
|
|
|
$me->install_dispatcher( |
|
431
|
|
|
|
|
|
|
$target, |
|
432
|
|
|
|
|
|
|
$method_name, |
|
433
|
14
|
50
|
|
|
|
76
|
$first ? $first->{'method'} : 0, |
|
434
|
|
|
|
|
|
|
); |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
} |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub install_monomethod { |
|
439
|
4
|
|
|
4
|
1
|
28
|
my ( $me, $target, $sub_name, %spec ) = ( shift, @_ ); |
|
440
|
|
|
|
|
|
|
|
|
441
|
4
|
|
50
|
|
|
29
|
$spec{alias} ||= []; |
|
442
|
4
|
50
|
|
|
|
13
|
$spec{alias} = [$spec{alias}] if !ref $spec{alias}; |
|
443
|
4
|
|
|
|
|
7
|
unshift @{$spec{alias}}, $sub_name; |
|
|
4
|
|
|
|
|
12
|
|
|
444
|
|
|
|
|
|
|
|
|
445
|
4
|
|
|
|
|
23
|
$me->install_candidate($target, undef, no_dispatcher => 1, %spec, is_monomethod => 1); |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
my %hooked; |
|
449
|
|
|
|
|
|
|
my $DECLARATION_ORDER = 0; |
|
450
|
|
|
|
|
|
|
sub install_candidate { |
|
451
|
106
|
|
|
106
|
1
|
895
|
my ( $me, $target, $sub_name, %spec ) = ( shift, @_ ); |
|
452
|
106
|
|
|
|
|
509
|
$me->_extract_type_params_spec( $target, $sub_name, \%spec ); |
|
453
|
|
|
|
|
|
|
|
|
454
|
106
|
|
|
|
|
308
|
my $is_method = $spec{method}; |
|
455
|
|
|
|
|
|
|
|
|
456
|
106
|
100
|
|
|
|
352
|
if ( $spec{want} ) { |
|
457
|
|
|
|
|
|
|
my @canonical = |
|
458
|
|
|
|
|
|
|
map { |
|
459
|
1
|
|
|
|
|
4
|
( my $x = $_ ) =~ s/^NON/~/; |
|
460
|
1
|
50
|
|
|
|
10
|
$_ eq '~VOID' ? qw( SCALAR LIST ) : |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
$_ eq '~LIST' ? qw( SCALAR VOID ) : |
|
462
|
|
|
|
|
|
|
$_ eq '~SCALAR' ? qw( LIST VOID ) : $_ |
|
463
|
|
|
|
|
|
|
} |
|
464
|
1
|
|
|
|
|
5
|
map { split /,/, $_ } |
|
465
|
1
|
|
|
|
|
5
|
map { uc $_ } |
|
466
|
1
|
50
|
|
|
|
8
|
is_ArrayRef($spec{want}) ? @{$spec{want}} : $spec{want}; |
|
|
0
|
|
|
|
|
0
|
|
|
467
|
1
|
|
|
|
|
4
|
$spec{want} = \@canonical; |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
|
|
470
|
106
|
|
|
|
|
286
|
$spec{declaration_order} = ++$DECLARATION_ORDER; |
|
471
|
|
|
|
|
|
|
|
|
472
|
106
|
100
|
|
|
|
760
|
$me->_add_multimethod_candidate($target, $sub_name, \%spec) |
|
473
|
|
|
|
|
|
|
if defined $sub_name; |
|
474
|
|
|
|
|
|
|
|
|
475
|
106
|
100
|
|
|
|
330
|
if ($spec{alias}) { |
|
476
|
|
|
|
|
|
|
my @aliases = is_ArrayRef( $spec{alias} ) |
|
477
|
4
|
|
|
|
|
14
|
? @{ $spec{alias} } |
|
478
|
16
|
100
|
|
|
|
140
|
: $spec{alias}; |
|
479
|
|
|
|
|
|
|
|
|
480
|
16
|
|
|
|
|
40
|
my ($check, @sig); |
|
481
|
16
|
50
|
|
|
|
117
|
if (is_CodeRef $spec{signature}) { |
|
482
|
0
|
|
|
|
|
0
|
$check = $spec{signature}; |
|
483
|
|
|
|
|
|
|
} |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
my %sig_spec = ( |
|
486
|
16
|
|
|
|
|
136
|
%{ $spec{signature_spec} }, |
|
487
|
|
|
|
|
|
|
goto_next => |
|
488
|
|
|
|
|
|
|
defined($spec{code}) ? $spec{code} : |
|
489
|
0
|
|
|
0
|
|
0
|
is_CodeRef($spec{die}) ? sub { require Carp; Carp::croak($spec{die}->()) } : |
|
|
0
|
|
|
|
|
0
|
|
|
490
|
0
|
|
|
0
|
|
0
|
defined($spec{die}) ? sub { require Carp; Carp::croak($spec{die}) } : |
|
|
0
|
|
|
|
|
0
|
|
|
491
|
0
|
|
|
0
|
|
0
|
sub { $spec{return} }, |
|
492
|
16
|
0
|
|
|
|
46
|
); |
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
493
|
16
|
|
|
|
|
50
|
my $code = sprintf( |
|
494
|
|
|
|
|
|
|
q{ |
|
495
|
|
|
|
|
|
|
package %s; |
|
496
|
|
|
|
|
|
|
sub { |
|
497
|
|
|
|
|
|
|
$check ||= Type::Params::signature( %%sig_spec ); |
|
498
|
|
|
|
|
|
|
goto $check; |
|
499
|
|
|
|
|
|
|
} |
|
500
|
|
|
|
|
|
|
}, |
|
501
|
|
|
|
|
|
|
$target, |
|
502
|
|
|
|
|
|
|
); |
|
503
|
16
|
|
|
|
|
156
|
my $coderef = do { |
|
504
|
16
|
|
|
|
|
46
|
local $@; |
|
505
|
16
|
50
|
66
|
59
|
|
2346
|
eval $code or die $@, |
|
|
50
|
|
|
|
|
41915
|
|
|
|
50
|
|
|
|
|
622100
|
|
|
506
|
|
|
|
|
|
|
}; |
|
507
|
16
|
|
|
|
|
55
|
for my $alias (@aliases) { |
|
508
|
16
|
|
|
|
|
31
|
my $existing = do { |
|
509
|
23
|
|
|
23
|
|
11248
|
no strict 'refs'; |
|
|
23
|
|
|
|
|
55
|
|
|
|
23
|
|
|
|
|
15834
|
|
|
510
|
16
|
|
|
|
|
130
|
exists(&{"$target\::$alias"}) |
|
511
|
16
|
100
|
|
|
|
28
|
? \&{"$target\::$alias"} |
|
|
4
|
|
|
|
|
18
|
|
|
512
|
|
|
|
|
|
|
: undef; |
|
513
|
|
|
|
|
|
|
}; |
|
514
|
16
|
100
|
|
|
|
67
|
if ($existing) { |
|
515
|
4
|
100
|
66
|
|
|
72
|
my $kind = ($spec{is_monomethod} && ($alias eq $aliases[0])) |
|
516
|
|
|
|
|
|
|
? 'Monomethod' |
|
517
|
|
|
|
|
|
|
: 'Alias'; |
|
518
|
4
|
|
|
|
|
33
|
require Carp; |
|
519
|
4
|
|
|
|
|
1150
|
Carp::croak("$kind conflicts with existing method $target\::$alias, bailing out"); |
|
520
|
|
|
|
|
|
|
} |
|
521
|
12
|
|
|
|
|
68
|
$me->_install_coderef( $target, $alias, $coderef ); |
|
522
|
|
|
|
|
|
|
} |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
$me->install_dispatcher($target, $sub_name, $is_method) |
|
526
|
102
|
100
|
100
|
|
|
795
|
if defined $sub_name && !$spec{no_dispatcher}; |
|
527
|
|
|
|
|
|
|
|
|
528
|
98
|
100
|
100
|
|
|
741
|
if ( !$hooked{$target} and 'Role::Hooks'->is_role($target) ) { |
|
529
|
|
|
|
|
|
|
'Role::Hooks'->after_apply($target, sub { |
|
530
|
20
|
|
|
20
|
|
11038
|
my ($rolepkg, $consumerpkg) = @_; |
|
531
|
20
|
|
|
|
|
91
|
$me->copy_package_candidates($rolepkg => $consumerpkg); |
|
532
|
20
|
100
|
|
|
|
90
|
$me->install_missing_dispatchers($consumerpkg) |
|
533
|
|
|
|
|
|
|
unless 'Role::Hooks'->is_role($consumerpkg); |
|
534
|
10
|
|
|
|
|
302
|
}); |
|
535
|
10
|
|
|
|
|
2777
|
$hooked{$target}++; |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
} |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
{ |
|
540
|
|
|
|
|
|
|
my %CLEANUP; |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
sub _install_coderef { |
|
543
|
60
|
|
|
60
|
|
146
|
my $me = shift; |
|
544
|
60
|
|
|
|
|
266
|
my ($target, $sub_name, $coderef) = @_; |
|
545
|
60
|
100
|
|
|
|
540
|
if (is_ScalarRef $sub_name) { |
|
|
|
50
|
|
|
|
|
|
|
546
|
14
|
100
|
33
|
|
|
138
|
if (is_Undef $$sub_name) { |
|
|
|
50
|
|
|
|
|
|
|
547
|
4
|
|
|
|
|
30
|
set_subname("$target\::__ANON__", $coderef); |
|
548
|
4
|
|
|
|
|
69
|
bless( $coderef, $me ); |
|
549
|
4
|
|
|
|
|
55
|
$CLEANUP{"$coderef"} = [ $target, refaddr($sub_name) ]; |
|
550
|
4
|
|
|
|
|
40
|
return( $$sub_name = $coderef ); |
|
551
|
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
elsif (is_CodeRef $$sub_name or is_Object $$sub_name) { |
|
553
|
10
|
50
|
|
|
|
40
|
if ( $me->known_dispatcher($$sub_name) ) { |
|
554
|
10
|
|
|
|
|
26
|
return $$sub_name; |
|
555
|
|
|
|
|
|
|
} |
|
556
|
|
|
|
|
|
|
else { |
|
557
|
0
|
|
|
|
|
0
|
require Carp; |
|
558
|
0
|
|
|
|
|
0
|
Carp::croak(sprintf( |
|
559
|
|
|
|
|
|
|
'Sub name was a reference to an unknown coderef or object: %s', |
|
560
|
|
|
|
|
|
|
$$sub_name, |
|
561
|
|
|
|
|
|
|
)); |
|
562
|
|
|
|
|
|
|
} |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
} |
|
565
|
|
|
|
|
|
|
elsif (is_Str $sub_name) { |
|
566
|
23
|
|
|
23
|
|
197
|
no strict 'refs'; |
|
|
23
|
|
|
|
|
69
|
|
|
|
23
|
|
|
|
|
7624
|
|
|
567
|
46
|
|
|
|
|
131
|
my $qname = "$target\::$sub_name"; |
|
568
|
46
|
|
|
|
|
279
|
*$qname = set_subname($qname, $coderef); |
|
569
|
46
|
|
|
|
|
1242
|
return $coderef; |
|
570
|
|
|
|
|
|
|
} |
|
571
|
0
|
|
|
|
|
0
|
require Carp; |
|
572
|
0
|
|
|
|
|
0
|
Carp::croak(sprintf( |
|
573
|
|
|
|
|
|
|
'Expected string or reference to coderef as sub name, but got: %s %s', |
|
574
|
|
|
|
|
|
|
$sub_name, |
|
575
|
|
|
|
|
|
|
)); |
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sub DESTROY { |
|
579
|
4
|
|
|
4
|
|
3393
|
my $blessed_coderef = shift; |
|
580
|
4
|
50
|
|
|
|
9
|
my ( $target, $sub_name ) = @{ $CLEANUP{"$blessed_coderef"} or [] }; |
|
|
4
|
|
|
|
|
30
|
|
|
581
|
4
|
50
|
33
|
|
|
142
|
if ( $target and $sub_name ) { |
|
582
|
4
|
|
|
|
|
19
|
$blessed_coderef->_clear_multimethod_candidates_ref($target, $sub_name); |
|
583
|
|
|
|
|
|
|
} |
|
584
|
4
|
|
|
|
|
105
|
return; |
|
585
|
|
|
|
|
|
|
} |
|
586
|
|
|
|
|
|
|
} |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
sub install_dispatcher { |
|
589
|
86
|
|
|
86
|
1
|
192
|
my $me = shift; |
|
590
|
86
|
|
|
|
|
220
|
my ($target, $sub_name, $is_method) = @_; |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
exists &mro::get_linear_isa |
|
593
|
0
|
|
|
|
|
0
|
or eval { require mro } |
|
594
|
86
|
50
|
33
|
|
|
439
|
or do { require MRO::Compat }; |
|
|
0
|
|
|
|
|
0
|
|
|
595
|
|
|
|
|
|
|
|
|
596
|
86
|
|
|
|
|
202
|
my $existing = do { |
|
597
|
23
|
|
|
23
|
|
194
|
no strict 'refs'; |
|
|
23
|
|
|
|
|
54
|
|
|
|
23
|
|
|
|
|
41803
|
|
|
598
|
86
|
|
|
|
|
553
|
exists(&{"$target\::$sub_name"}) |
|
599
|
86
|
100
|
|
|
|
147
|
? \&{"$target\::$sub_name"} |
|
|
38
|
|
|
|
|
173
|
|
|
600
|
|
|
|
|
|
|
: undef; |
|
601
|
|
|
|
|
|
|
}; |
|
602
|
|
|
|
|
|
|
|
|
603
|
86
|
50
|
|
|
|
251
|
return if !defined $sub_name; |
|
604
|
|
|
|
|
|
|
|
|
605
|
86
|
100
|
100
|
|
|
566
|
if ($existing and $me->known_dispatcher($existing)) { |
|
|
|
100
|
|
|
|
|
|
|
606
|
34
|
|
|
|
|
106
|
return $me; # already installed |
|
607
|
|
|
|
|
|
|
} |
|
608
|
|
|
|
|
|
|
elsif ($existing) { |
|
609
|
4
|
|
|
|
|
39
|
require Carp; |
|
610
|
4
|
|
|
|
|
1013
|
Carp::croak("Multimethod conflicts with monomethod $target\::$sub_name, bailing out"); |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
|
|
613
|
48
|
100
|
100
|
|
|
832
|
my $code = sprintf( |
|
614
|
|
|
|
|
|
|
q{ |
|
615
|
|
|
|
|
|
|
package %s; |
|
616
|
|
|
|
|
|
|
sub { |
|
617
|
|
|
|
|
|
|
@_ = (%s, %s, %s, %d, [@_], wantarray); |
|
618
|
|
|
|
|
|
|
goto $next; |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
}, |
|
621
|
|
|
|
|
|
|
$target, # package %s |
|
622
|
|
|
|
|
|
|
B::perlstring($me), # $_[0] |
|
623
|
|
|
|
|
|
|
B::perlstring($target), # $_[1] |
|
624
|
|
|
|
|
|
|
ref($sub_name) # $_[2] |
|
625
|
|
|
|
|
|
|
? refaddr($sub_name) |
|
626
|
|
|
|
|
|
|
: B::perlstring("$sub_name"), |
|
627
|
|
|
|
|
|
|
$is_method || 0, # $_[3] |
|
628
|
|
|
|
|
|
|
); |
|
629
|
|
|
|
|
|
|
|
|
630
|
48
|
|
|
|
|
125
|
my $coderef = do { |
|
631
|
48
|
|
|
|
|
91
|
local $@; |
|
632
|
48
|
|
|
|
|
471
|
my $next = $me->can('dispatch'); |
|
633
|
48
|
50
|
|
16
|
|
7384
|
eval $code or die $@; |
|
|
7
|
|
|
|
|
6040
|
|
|
|
7
|
|
|
|
|
32
|
|
|
634
|
|
|
|
|
|
|
}; |
|
635
|
|
|
|
|
|
|
|
|
636
|
48
|
|
|
|
|
287
|
$me->_install_coderef($target, $sub_name, $coderef); |
|
637
|
48
|
|
|
|
|
390
|
$me->_mark_as_dispatcher($coderef); |
|
638
|
48
|
|
|
|
|
293
|
return $coderef; |
|
639
|
|
|
|
|
|
|
} |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
sub dispatch { |
|
642
|
149
|
|
|
149
|
0
|
13268
|
my $me = shift; |
|
643
|
149
|
|
|
|
|
560
|
my ($pkg, $method_name, $is_method, $argv, $wantarray) = @_; |
|
644
|
139
|
50
|
|
|
|
17643
|
$wantarray = wantarray if @_ < 5; |
|
645
|
|
|
|
|
|
|
|
|
646
|
139
|
|
|
|
|
361
|
my $search_from = $pkg; |
|
647
|
136
|
100
|
100
|
|
|
14421
|
if ( $is_method and is_Object $argv->[0] ) { |
|
|
|
100
|
100
|
|
|
|
|
|
648
|
|
|
|
|
|
|
# object method; reset package search from invocant class |
|
649
|
91
|
|
|
|
|
297
|
$search_from = ref $argv->[0]; |
|
650
|
|
|
|
|
|
|
} |
|
651
|
|
|
|
|
|
|
elsif ( $is_method and is_ClassName $argv->[0] ) { |
|
652
|
|
|
|
|
|
|
# class method; reset package search from invocant class |
|
653
|
17
|
|
|
|
|
5646
|
$search_from = $argv->[0]; |
|
654
|
|
|
|
|
|
|
} |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
my ($winner, $new_argv) = $me->pick_candidate( |
|
657
|
|
|
|
|
|
|
$CACHE{"$pkg/$search_from/$method_name/$is_method"} ||= |
|
658
|
|
|
|
|
|
|
[ $me->get_all_multimethod_candidates($search_from, $method_name, $is_method) ], |
|
659
|
|
|
|
|
|
|
$argv, |
|
660
|
|
|
|
|
|
|
$wantarray ? LIST : defined($wantarray) ? SCALAR : VOID, |
|
661
|
130
|
100
|
100
|
|
|
1406
|
) or do { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
662
|
15
|
|
|
|
|
8418
|
require Carp; |
|
663
|
15
|
|
|
|
|
1530
|
Carp::croak('Multimethod could not find candidate to dispatch to, stopped'); |
|
664
|
|
|
|
|
|
|
}; |
|
665
|
|
|
|
|
|
|
|
|
666
|
126
|
50
|
|
|
|
8720
|
if ( my $next = $winner->{code} ) { |
|
|
|
0
|
|
|
|
|
|
|
667
|
126
|
|
|
|
|
463
|
@_ = @$new_argv; |
|
668
|
117
|
|
|
|
|
762
|
goto $next; |
|
669
|
|
|
|
|
|
|
} |
|
670
|
|
|
|
|
|
|
elsif ( defined $winner->{die} ) { |
|
671
|
0
|
|
|
|
|
0
|
require Carp; |
|
672
|
0
|
0
|
|
|
|
0
|
Carp::croak( is_CodeRef( $winner->{die} ) ? $winner->{die}->() : $winner->{die} ); |
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
else { |
|
675
|
0
|
|
|
|
|
0
|
return $winner->{return}; |
|
676
|
|
|
|
|
|
|
} |
|
677
|
|
|
|
|
|
|
} |
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
# Optimization for simple signatures: those consisting of only non-coercing positional parameters. |
|
680
|
|
|
|
|
|
|
my $smiple_keys = Enum[qw/ package subname method pos positional /]; |
|
681
|
|
|
|
|
|
|
sub _maybe_make_smiple { |
|
682
|
162
|
|
|
178
|
|
575
|
my ( $me, $candidate ) = @_; |
|
683
|
162
|
100
|
|
|
|
624
|
return if $candidate->{smiple}; |
|
684
|
158
|
100
|
|
|
|
394
|
return unless $smiple_keys->all( keys %{ $candidate->{signature_spec} } ); |
|
|
158
|
|
|
|
|
1663
|
|
|
685
|
|
|
|
|
|
|
my @types = |
|
686
|
244
|
0
|
|
|
|
1072
|
map { is_Bool( $_ ) ? ( $_ ? Any : Optional[Any] ) : $_ } |
|
|
|
50
|
|
|
|
|
|
|
687
|
152
|
50
|
33
|
|
|
15025
|
@{ $candidate->{signature_spec}{pos} or $candidate->{signature_spec}{positional} or [] }; |
|
|
152
|
|
|
|
|
1309
|
|
|
688
|
152
|
50
|
|
|
|
670
|
return unless TypeTiny->all( @types ); |
|
689
|
152
|
100
|
|
|
|
20676
|
if ( TypeTiny->check( $candidate->{signature_spec}{method} ) ) { |
|
|
|
50
|
|
|
|
|
|
|
690
|
136
|
|
|
|
|
3327
|
unshift @types, $candidate->{signature_spec}{method}; |
|
691
|
|
|
|
|
|
|
} |
|
692
|
|
|
|
|
|
|
elsif ( $candidate->{signature_spec}{method} ) { |
|
693
|
0
|
|
|
|
|
0
|
unshift @types, Any; |
|
694
|
|
|
|
|
|
|
} |
|
695
|
152
|
100
|
|
|
|
646
|
return if grep { $_->has_coercion } @types; |
|
|
380
|
|
|
|
|
4833
|
|
|
696
|
148
|
|
|
|
|
3817
|
$candidate->{smiple} = Tuple->of( @types )->compiled_check; |
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sub pick_candidate { |
|
700
|
123
|
|
|
136
|
1
|
404
|
my ( $me, $candidates, $argv, $wantarray ) = ( shift, @_ ); |
|
701
|
|
|
|
|
|
|
|
|
702
|
123
|
|
|
|
|
216
|
my @remaining = @{ $candidates }; |
|
|
123
|
|
|
|
|
438
|
|
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
# Compile signatures into something useful. (Cached.) |
|
705
|
|
|
|
|
|
|
# |
|
706
|
|
|
|
|
|
|
|
|
707
|
123
|
|
|
|
|
326
|
for my $candidate (@remaining) { |
|
708
|
754
|
100
|
|
|
|
463282
|
next if $candidate->{compiled}; |
|
709
|
166
|
100
|
|
|
|
1246
|
if ( is_CodeRef $candidate->{signature} ) { |
|
710
|
4
|
|
|
|
|
19
|
$candidate->{compiled}{closure} = $candidate->{signature}; |
|
711
|
4
|
|
|
|
|
11
|
$candidate->{compiled}{min_args} = 0; |
|
712
|
4
|
|
|
|
|
15
|
$candidate->{compiled}{max_args} = undef; |
|
713
|
|
|
|
|
|
|
} |
|
714
|
|
|
|
|
|
|
else { |
|
715
|
|
|
|
|
|
|
$candidate->{compiled} = Type::Params::signature( |
|
716
|
162
|
|
|
|
|
301
|
%{ $candidate->{signature_spec} }, |
|
|
162
|
|
|
|
|
1304
|
|
|
717
|
|
|
|
|
|
|
want_details => 1, |
|
718
|
|
|
|
|
|
|
); |
|
719
|
162
|
|
|
|
|
2266987
|
$me->_maybe_make_smiple( $candidate ); |
|
720
|
|
|
|
|
|
|
} |
|
721
|
|
|
|
|
|
|
} |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
# Weed out signatures that cannot match because of |
|
724
|
|
|
|
|
|
|
# argument count. |
|
725
|
|
|
|
|
|
|
# |
|
726
|
|
|
|
|
|
|
|
|
727
|
123
|
|
|
|
|
41592
|
my $argc = @$argv; |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
@remaining = |
|
730
|
735
|
100
|
|
|
|
1639
|
grep { $_->{if} ? &{$_->{if}} : 1 } |
|
|
3
|
|
|
|
|
11
|
|
|
731
|
736
|
100
|
66
|
9
|
|
2041
|
grep { ($_->{want} and $wantarray) ? (!!any { $wantarray eq $_ } @{$_->{want}}) : 1 } |
|
|
2
|
|
|
|
|
13
|
|
|
|
2
|
|
|
|
|
13
|
|
|
732
|
|
|
|
|
|
|
grep { |
|
733
|
123
|
|
|
|
|
354
|
(defined $_->{compiled}{min_args} and $_->{compiled}{min_args} > $argc) ? 0 : |
|
734
|
754
|
100
|
66
|
|
|
5441
|
(defined $_->{compiled}{max_args} and $_->{compiled}{max_args} < $argc) ? 0 : 1; |
|
|
|
100
|
100
|
|
|
|
|
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
@remaining; |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# Weed out signatures that cannot match because |
|
739
|
|
|
|
|
|
|
# they fail type checks, etc |
|
740
|
|
|
|
|
|
|
# |
|
741
|
|
|
|
|
|
|
|
|
742
|
123
|
|
|
|
|
252
|
my %returns; |
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
@remaining = grep { |
|
745
|
123
|
100
|
|
|
|
245
|
if ( my $smiple = $_->{smiple} ) { |
|
|
733
|
|
|
|
|
53843
|
|
|
746
|
667
|
100
|
100
|
|
|
3607
|
!ref($smiple) || $smiple->($argv) ? ($returns{"$_"} = $argv) : (); |
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
else { |
|
749
|
66
|
|
|
|
|
144
|
eval { |
|
750
|
66
|
|
|
|
|
273
|
$returns{"$_"} = [ $_->{compiled}{closure}->(@$argv) ]; |
|
751
|
30
|
|
|
|
|
581
|
1; |
|
752
|
|
|
|
|
|
|
}; |
|
753
|
|
|
|
|
|
|
} |
|
754
|
|
|
|
|
|
|
} @remaining; |
|
755
|
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# Various techniques to cope with @remaining > 1... |
|
757
|
|
|
|
|
|
|
# |
|
758
|
|
|
|
|
|
|
|
|
759
|
123
|
100
|
|
|
|
414
|
if (@remaining > 1) { |
|
760
|
23
|
|
|
23
|
|
287
|
no warnings qw(uninitialized numeric); |
|
|
23
|
|
|
|
|
48
|
|
|
|
23
|
|
|
|
|
8020
|
|
|
761
|
|
|
|
|
|
|
# Calculate signature constrainedness score. (Cached.) |
|
762
|
58
|
|
|
|
|
117
|
my $max_score; |
|
763
|
58
|
|
|
|
|
174
|
for my $candidate (@remaining) { |
|
764
|
164
|
|
|
|
|
325
|
my $score = $candidate->{score}; |
|
765
|
164
|
100
|
|
|
|
428
|
if ( not defined $score ) { |
|
766
|
74
|
|
|
|
|
315
|
my $slurpyAny = Slurpy[Any]; |
|
767
|
74
|
|
|
|
|
68643
|
$score = 0; |
|
768
|
|
|
|
|
|
|
my @sig = map { |
|
769
|
74
|
100
|
|
|
|
197
|
is_ArrayRef( $candidate->{signature_spec}{$_} ) ? @{ $candidate->{signature_spec}{$_} } : (); |
|
|
222
|
|
|
|
|
1246
|
|
|
|
70
|
|
|
|
|
240
|
|
|
770
|
|
|
|
|
|
|
} qw(positional pos named); |
|
771
|
74
|
|
|
|
|
214
|
foreach my $type ( @sig ) { |
|
772
|
100
|
50
|
|
|
|
484
|
next unless is_Object $type; |
|
773
|
100
|
100
|
|
|
|
424
|
next if $type == $slurpyAny; |
|
774
|
96
|
|
|
|
|
137312
|
my @real_parents = grep !$_->_is_null_constraint, $type, $type->parents; |
|
775
|
96
|
|
|
|
|
7349
|
$score += @real_parents; |
|
776
|
|
|
|
|
|
|
} |
|
777
|
74
|
100
|
100
|
|
|
838
|
$score += 100_000 if $candidate->{want} || $candidate->{if}; |
|
778
|
74
|
|
|
|
|
395
|
$candidate->{score} = $score; |
|
779
|
|
|
|
|
|
|
}; |
|
780
|
164
|
|
|
|
|
5103
|
$max_score = max( grep defined, $score, $max_score ); |
|
781
|
|
|
|
|
|
|
} |
|
782
|
|
|
|
|
|
|
# Only keep those with (equal) highest score |
|
783
|
58
|
|
|
|
|
145
|
@remaining = grep { $_->{score} == $max_score } @remaining; |
|
|
164
|
|
|
|
|
469
|
|
|
784
|
|
|
|
|
|
|
} |
|
785
|
|
|
|
|
|
|
|
|
786
|
123
|
100
|
|
|
|
420
|
if (@remaining > 1) { |
|
787
|
|
|
|
|
|
|
# Only keep those from the most derived class |
|
788
|
23
|
|
|
23
|
|
223
|
no warnings qw(uninitialized numeric); |
|
|
23
|
|
|
|
|
54
|
|
|
|
23
|
|
|
|
|
2635
|
|
|
789
|
20
|
|
|
|
|
129
|
my $max_score = max( map $_->{height}, @remaining ); |
|
790
|
20
|
|
|
|
|
51
|
@remaining = grep { $_->{height} == $max_score } @remaining; |
|
|
58
|
|
|
|
|
127
|
|
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
|
|
793
|
123
|
100
|
|
|
|
432
|
if (@remaining > 1) { |
|
794
|
|
|
|
|
|
|
# Only keep those from the most non-role-like packages |
|
795
|
23
|
|
|
23
|
|
162
|
no warnings qw(uninitialized numeric); |
|
|
23
|
|
|
|
|
70
|
|
|
|
23
|
|
|
|
|
2541
|
|
|
796
|
10
|
|
|
|
|
60
|
my $min_score = min( map $_->{copied}, @remaining ); |
|
797
|
10
|
|
|
|
|
25
|
@remaining = grep { $_->{copied} == $min_score } @remaining; |
|
|
38
|
|
|
|
|
79
|
|
|
798
|
|
|
|
|
|
|
} |
|
799
|
|
|
|
|
|
|
|
|
800
|
123
|
100
|
|
|
|
340
|
if (@remaining > 1) { |
|
801
|
|
|
|
|
|
|
# Argh! Still got multiple candidates! Just choose whichever |
|
802
|
|
|
|
|
|
|
# was declared first... |
|
803
|
23
|
|
|
23
|
|
176
|
no warnings qw(uninitialized numeric); |
|
|
23
|
|
|
|
|
64
|
|
|
|
23
|
|
|
|
|
11009
|
|
|
804
|
8
|
|
|
|
|
36
|
my $min_score = min( map $_->{declaration_order}, @remaining ); |
|
805
|
8
|
|
|
|
|
19
|
@remaining = grep { $_->{declaration_order} == $min_score } @remaining; |
|
|
34
|
|
|
|
|
60
|
|
|
806
|
|
|
|
|
|
|
} |
|
807
|
|
|
|
|
|
|
|
|
808
|
123
|
50
|
|
|
|
435
|
wantarray or die 'MUST BE CALLED IN LIST CONTEXT'; |
|
809
|
|
|
|
|
|
|
|
|
810
|
123
|
100
|
|
|
|
344
|
return unless @remaining; |
|
811
|
117
|
|
|
|
|
1072
|
return ( $remaining[0], $returns{''.$remaining[0]} ); |
|
812
|
|
|
|
|
|
|
} |
|
813
|
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
1; |
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
__END__ |
|
817
|
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
=pod |
|
819
|
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=encoding utf-8 |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=head1 NAME |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
Sub::MultiMethod - yet another implementation of multimethods |
|
825
|
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
827
|
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
How to generate JSON (albeit with very naive string quoting) using |
|
829
|
|
|
|
|
|
|
multimethods: |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
use v5.20; |
|
832
|
|
|
|
|
|
|
use strict; |
|
833
|
|
|
|
|
|
|
use warnings; |
|
834
|
|
|
|
|
|
|
use experimental 'signatures'; |
|
835
|
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
package My::JSON { |
|
837
|
|
|
|
|
|
|
use Moo; |
|
838
|
|
|
|
|
|
|
use Sub::MultiMethod qw(multimethod); |
|
839
|
|
|
|
|
|
|
use Types::Standard -types; |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
multimethod stringify => ( |
|
842
|
|
|
|
|
|
|
positional => [ Undef ], |
|
843
|
|
|
|
|
|
|
code => sub ( $self, $undef ) { |
|
844
|
|
|
|
|
|
|
return 'null'; |
|
845
|
|
|
|
|
|
|
}, |
|
846
|
|
|
|
|
|
|
); |
|
847
|
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
multimethod stringify => ( |
|
849
|
|
|
|
|
|
|
positional => [ ScalarRef[Bool] ], |
|
850
|
|
|
|
|
|
|
code => sub ( $self, $bool ) { |
|
851
|
|
|
|
|
|
|
return $$bool ? 'true' : 'false'; |
|
852
|
|
|
|
|
|
|
}, |
|
853
|
|
|
|
|
|
|
); |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
multimethod stringify => ( |
|
856
|
|
|
|
|
|
|
alias => "stringify_str", |
|
857
|
|
|
|
|
|
|
positional => [ Str ], |
|
858
|
|
|
|
|
|
|
code => sub ( $self, $str ) { |
|
859
|
|
|
|
|
|
|
return sprintf( q<"%s">, quotemeta($str) ); |
|
860
|
|
|
|
|
|
|
}, |
|
861
|
|
|
|
|
|
|
); |
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
multimethod stringify => ( |
|
864
|
|
|
|
|
|
|
positional => [ Num ], |
|
865
|
|
|
|
|
|
|
code => sub ( $self, $n ) { |
|
866
|
|
|
|
|
|
|
return $n; |
|
867
|
|
|
|
|
|
|
}, |
|
868
|
|
|
|
|
|
|
); |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
multimethod stringify => ( |
|
871
|
|
|
|
|
|
|
positional => [ ArrayRef ], |
|
872
|
|
|
|
|
|
|
code => sub ( $self, $arr ) { |
|
873
|
|
|
|
|
|
|
return sprintf( |
|
874
|
|
|
|
|
|
|
q<[%s]>, |
|
875
|
|
|
|
|
|
|
join( q<,>, map( $self->stringify($_), @$arr ) ) |
|
876
|
|
|
|
|
|
|
); |
|
877
|
|
|
|
|
|
|
}, |
|
878
|
|
|
|
|
|
|
); |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
multimethod stringify => ( |
|
881
|
|
|
|
|
|
|
positional => [ HashRef ], |
|
882
|
|
|
|
|
|
|
code => sub ( $self, $hash ) { |
|
883
|
|
|
|
|
|
|
return sprintf( |
|
884
|
|
|
|
|
|
|
q<{%s}>, |
|
885
|
|
|
|
|
|
|
join( |
|
886
|
|
|
|
|
|
|
q<,>, |
|
887
|
|
|
|
|
|
|
map sprintf( |
|
888
|
|
|
|
|
|
|
q<%s:%s>, |
|
889
|
|
|
|
|
|
|
$self->stringify_str($_), |
|
890
|
|
|
|
|
|
|
$self->stringify( $hash->{$_} ) |
|
891
|
|
|
|
|
|
|
), sort keys %$hash, |
|
892
|
|
|
|
|
|
|
) |
|
893
|
|
|
|
|
|
|
); |
|
894
|
|
|
|
|
|
|
}, |
|
895
|
|
|
|
|
|
|
); |
|
896
|
|
|
|
|
|
|
} |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
my $json = My::JSON->new; |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
say $json->stringify( { |
|
901
|
|
|
|
|
|
|
foo => 123, |
|
902
|
|
|
|
|
|
|
bar => [ 1, 2, 3 ], |
|
903
|
|
|
|
|
|
|
baz => \1, |
|
904
|
|
|
|
|
|
|
quux => { xyzzy => 666 }, |
|
905
|
|
|
|
|
|
|
} ); |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
While this example requires Perl 5.20+, Sub::MultiMethod is tested and works |
|
908
|
|
|
|
|
|
|
on Perl 5.8.1 and above. |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
Sub::MultiMethod focusses on implementing the dispatching of multimethods |
|
913
|
|
|
|
|
|
|
well and is less concerned with providing a nice syntax for setting them |
|
914
|
|
|
|
|
|
|
up. That said, the syntax provided is inspired by Moose's C<has> keyword |
|
915
|
|
|
|
|
|
|
and hopefully not entirely horrible. |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=head2 Functions |
|
918
|
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Sub::MultiMethod exports nothing by default. You can import the functions |
|
920
|
|
|
|
|
|
|
you want by listing them in the C<use> statement: |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
use Sub::MultiMethod "multimethod"; |
|
923
|
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
You can rename functions: |
|
925
|
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
use Sub::MultiMethod "multimethod" => { -as => "mm" }; |
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
You can import everything using C<< -all >>: |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
use Sub::MultiMethod -all; |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
Sub::MultiMethod also offers an API for setting up multimethods for a |
|
933
|
|
|
|
|
|
|
class, in which case, you don't need to import anything. |
|
934
|
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
=head3 C<< multimethod $name => %spec >> |
|
936
|
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
The specification supports the same options as L<Type::Params> v2 |
|
938
|
|
|
|
|
|
|
to specify a signature for the method, plus a few Sub::MultiMethod-specific |
|
939
|
|
|
|
|
|
|
options. Any options not included in the list below are passed through to |
|
940
|
|
|
|
|
|
|
Type::Params. (The options C<goto_next>, C<on_die>, C<message>, and |
|
941
|
|
|
|
|
|
|
C<want_*> are not supported.) |
|
942
|
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
=over |
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
=item C<< code >> I<< (CodeRef) >> |
|
946
|
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
Conceptually required, but see the C<return> and C<die> shortcuts. |
|
948
|
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
The sub to dispatch to. It will receive parameters in C<< @_ >> as you |
|
950
|
|
|
|
|
|
|
would expect, but these parameters have been passed through the signature |
|
951
|
|
|
|
|
|
|
already, so will have had defaults and coercions applied. |
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
An example for positional parameters: |
|
954
|
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
code => sub ( $self, $prefix, $match, $output ) { |
|
956
|
|
|
|
|
|
|
print { $output } $prefix; |
|
957
|
|
|
|
|
|
|
...; |
|
958
|
|
|
|
|
|
|
}, |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
An example for named parameters: |
|
961
|
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
code => sub ( $self, $arg ) { |
|
963
|
|
|
|
|
|
|
print { $arg->output } $arg->prefix; |
|
964
|
|
|
|
|
|
|
...; |
|
965
|
|
|
|
|
|
|
}, |
|
966
|
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
Note that C<< $arg >> is an object with methods for each named parameter. |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
Corresponding examples for older versions of Perl without signature support. |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
code => sub { |
|
972
|
|
|
|
|
|
|
my ( $self, $prefix, $match, $output ) = @_; |
|
973
|
|
|
|
|
|
|
print { $output } $prefix; |
|
974
|
|
|
|
|
|
|
...; |
|
975
|
|
|
|
|
|
|
}, |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
And: |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
code => sub { |
|
980
|
|
|
|
|
|
|
my ( $self, $arg ) = @_; |
|
981
|
|
|
|
|
|
|
print { $arg->output } $arg->prefix; |
|
982
|
|
|
|
|
|
|
...; |
|
983
|
|
|
|
|
|
|
}, |
|
984
|
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=item C<< return >> I<< (Value) >> |
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
Shortcut. |
|
988
|
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
You can use C<< return => "foo" >> as a shortcut for |
|
990
|
|
|
|
|
|
|
C<< code => sub { return "foo" } >>. This cannot be used |
|
991
|
|
|
|
|
|
|
to return refereneces. |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
=item C<< die >> I<< (CodeRef|Str) >> |
|
994
|
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
Shortcut. |
|
996
|
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
You can use C<< die => "foo" >> as a shortcut for |
|
998
|
|
|
|
|
|
|
C<< code => sub { require Carp; Carp::croak("foo") } >>. |
|
999
|
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
You can use C<< die => \&foo >> as a shortcut for |
|
1001
|
|
|
|
|
|
|
C<< code => sub { require Carp; Carp::croak(foo()) } >>. |
|
1002
|
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
=item C<< signature >> I<< (CodeRef) >> |
|
1004
|
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
Optional. |
|
1006
|
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
If C<signature> is set, then Sub::MultiMethod won't use L<Type::Params> |
|
1008
|
|
|
|
|
|
|
to build a signature for this multimethod candidate. It will treat the |
|
1009
|
|
|
|
|
|
|
coderef as an already-built signature. |
|
1010
|
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
A coderef signature is expected to take C<< @_ >>, throw an exception if |
|
1012
|
|
|
|
|
|
|
the arguments cannot be handled, and return C<< @_ >> (possibly after some |
|
1013
|
|
|
|
|
|
|
manipulation). |
|
1014
|
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
=item C<< alias >> I<< (Str|ArrayRef[Str]) >> |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
Optional. |
|
1018
|
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
Installs an alias for the candidate, bypassing multimethod dispatch. (But not |
|
1020
|
|
|
|
|
|
|
bypassing the checks, coercions, and defaults in the signature!) |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
=item C<< method >> I<< (Bool) >> |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
Optional, defaults to 1. |
|
1025
|
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
Indicates whether the multimethod should be treated as a method (i.e. with an |
|
1027
|
|
|
|
|
|
|
implied C<< $self >>). Defaults to true, but C<< method => 0 >> can be |
|
1028
|
|
|
|
|
|
|
given if you want multifuncs with no invocant. |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
Multisubs where some candidates are methods and others are non-methods are |
|
1031
|
|
|
|
|
|
|
not currently supported! (And probably never will be.) |
|
1032
|
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
=item C<< want >> I<< (Str|ArrayRef) >> |
|
1034
|
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
Optional. |
|
1036
|
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
Allows you to specify that a candidate only applies in certain contexts. |
|
1038
|
|
|
|
|
|
|
The context may be "VOID", "SCALAR", or "LIST". May alternatively be an |
|
1039
|
|
|
|
|
|
|
arrayref of contexts. "NONVOID" is a shortcut for C<< ["SCALAR","LIST"] >>. |
|
1040
|
|
|
|
|
|
|
"NONLIST" and "NONSCALAR" are also allowed. |
|
1041
|
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
=item C<< if >> I<< (CodeRef) >> |
|
1043
|
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
Optional. |
|
1045
|
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
Allows you to specify that a candidate only applies in certain conditions. |
|
1047
|
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
if => sub { $ENV{OSTYPE} eq 'linux' }, |
|
1049
|
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
The coderef is called with no parameters. It has no access to the multimethod's |
|
1051
|
|
|
|
|
|
|
C<< @_ >>. |
|
1052
|
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
=item C<< score >> I<< (Int) >> |
|
1054
|
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
Optional. |
|
1056
|
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
Overrides the constrainedness score calculated as described in the dispatch |
|
1058
|
|
|
|
|
|
|
technique. Most scores calculated that way will typically between 0 and 100. |
|
1059
|
|
|
|
|
|
|
Setting a score manually to something very high (e.g. 9999) will pretty much |
|
1060
|
|
|
|
|
|
|
guarantee that it gets chosen over other candidates when multiple signatures |
|
1061
|
|
|
|
|
|
|
match. Setting it to something low (e.g. -1) will mean it gets avoided. |
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=item C<< no_dispatcher >> I<< (Bool) >> |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
Optional. Defaults to true in roles, false otherwise. |
|
1066
|
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
If set to true, Sub::MultiMethods will register the candidate method |
|
1068
|
|
|
|
|
|
|
but won't install a dispatcher. You should mostly not worry about this |
|
1069
|
|
|
|
|
|
|
and accept the default. |
|
1070
|
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
=back |
|
1072
|
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
The C<< @_ >> passed to Sub::MultiMethod is pre-processed slightly |
|
1074
|
|
|
|
|
|
|
after C<< $name >> has been shifted off but before being interpreted as |
|
1075
|
|
|
|
|
|
|
the C<< %spec >> hash. Obviously hashes are expected to have string keys, |
|
1076
|
|
|
|
|
|
|
so if the first argument of C<< @_ >> is an arrayref or hashref, those |
|
1077
|
|
|
|
|
|
|
are interpreted as a positional or named signature respectively. |
|
1078
|
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
So the following are equivalent: |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
multimethod foo => [...] => %spec; |
|
1082
|
|
|
|
|
|
|
multimethod foo => ( positional => [...], %spec ); |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
And so are the following (note that the hashref becomes an arrayref): |
|
1085
|
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
multimethod foo => {...} => ( %spec ); |
|
1087
|
|
|
|
|
|
|
multimethod foo => ( named => [...], %spec ); |
|
1088
|
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
After this, if C<< @_ >> is an odd-sized list with a coderef in the last |
|
1090
|
|
|
|
|
|
|
position, the coderef is treated as the C<code> option. |
|
1091
|
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
So the following are equivalent: |
|
1093
|
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
multimethod foo => ( %spec ) => sub { ... }; |
|
1095
|
|
|
|
|
|
|
multimethod foo => ( %spec, code => sub { ... } ); |
|
1096
|
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
For the common case of: |
|
1098
|
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
multimethod foo => ( |
|
1100
|
|
|
|
|
|
|
positional => [...], |
|
1101
|
|
|
|
|
|
|
code => sub { |
|
1102
|
|
|
|
|
|
|
...; |
|
1103
|
|
|
|
|
|
|
}, |
|
1104
|
|
|
|
|
|
|
); |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
These combination of shortcuts allow it to be written as: |
|
1107
|
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
multimethod foo => [...] => sub { |
|
1109
|
|
|
|
|
|
|
...; |
|
1110
|
|
|
|
|
|
|
}; |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=head3 C<< monomethod $name => %spec >> |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
C<< monomethod($name, %spec) >> is basically just a shortcut for |
|
1115
|
|
|
|
|
|
|
C<< multimethod(undef, alias => $name, %spec) >> though with error |
|
1116
|
|
|
|
|
|
|
messages which don't mention it being an alias. |
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
=head3 C<< multifunction $name => %spec >> |
|
1119
|
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
Like C<multimethod> but defaults to C<< method => 0 >>. |
|
1121
|
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
=head3 C<< monofunction $name => %spec >> |
|
1123
|
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
Like C<monomethod> but defaults to C<< method => 0 >>. |
|
1125
|
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
=head3 C<< VOID >>, C<< SCALAR >>, C<< LIST >>, C<< NONVOID >>, C<< NONSCALAR >>, C<< NONLIST >> |
|
1127
|
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
Useful constants you can export to allow this to work: |
|
1129
|
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
want => NONVOID, |
|
1131
|
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
=head2 Dispatch Technique |
|
1133
|
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
When a multimethod is called, a list of packages to inspect for candidates |
|
1135
|
|
|
|
|
|
|
is obtained by crawling C<< @ISA >>. (For multifuncs, C<< @ISA >> is ignored.) |
|
1136
|
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
All candidates for the invoking class and all parent classes are considered. |
|
1138
|
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
If any parent class includes a mono-method (i.e. not a multimethod) of the |
|
1140
|
|
|
|
|
|
|
same name as this multimethod, then it is considered to have override any |
|
1141
|
|
|
|
|
|
|
candidates further along the C<< @ISA >> chain. (With multiple inheritance, |
|
1142
|
|
|
|
|
|
|
this could get confusing though!) Those further candidates will not be |
|
1143
|
|
|
|
|
|
|
considered, however the mono-method will be considered to be a candidate, |
|
1144
|
|
|
|
|
|
|
albeit one with a very low score. (See scoring later.) |
|
1145
|
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
Any candidates where it is clear they will not match based on parameter |
|
1147
|
|
|
|
|
|
|
count will be discarded immediately. |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
After that, the signatures of each are tried. If they throw an error, that |
|
1150
|
|
|
|
|
|
|
candidate will be discarded. |
|
1151
|
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
If there are still multiple possible candidates, they will be sorted based |
|
1153
|
|
|
|
|
|
|
on how constrained they are. |
|
1154
|
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
To determine how constrained they are, every type constraint in their |
|
1156
|
|
|
|
|
|
|
signature is assigned a score. B<Any> is 0. B<Defined> inherits from |
|
1157
|
|
|
|
|
|
|
B<Any>, so has score 1. B<Value> inherits from B<Defined>, so has score 2. |
|
1158
|
|
|
|
|
|
|
Etc. Some types inherit from a parent but without further constraining |
|
1159
|
|
|
|
|
|
|
the parent. (For example, B<Item> inherits from B<Any> but doesn't place |
|
1160
|
|
|
|
|
|
|
any additional constraints on values.) In these cases, the child type |
|
1161
|
|
|
|
|
|
|
has the same score as its parent. All these scores are added together |
|
1162
|
|
|
|
|
|
|
to get a single score for the candidate. For candidates where the |
|
1163
|
|
|
|
|
|
|
signature is a coderef, this is essentially a zero score for the |
|
1164
|
|
|
|
|
|
|
signature unless a score was specified explicitly. |
|
1165
|
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
The score has 100,000 added if C<want> or C<if> was specified. |
|
1167
|
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
If multiple candidates are equally constrained, child class candidates |
|
1169
|
|
|
|
|
|
|
beat parent class candidates; class candidates beat role candidates; |
|
1170
|
|
|
|
|
|
|
and the candidate that was declared earlier wins. |
|
1171
|
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
Method-resolution order (DFS/C3) is respected, though in Perl 5.8 under |
|
1173
|
|
|
|
|
|
|
very contrived conditions (calling a sub as a function when it was |
|
1174
|
|
|
|
|
|
|
defined as a method, but not passing a valid invocant as the first |
|
1175
|
|
|
|
|
|
|
parameter), MRO may not always work correctly. |
|
1176
|
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
Note that invocants are not part of the signature, so not taken into |
|
1178
|
|
|
|
|
|
|
account when calculating scores, but because child class candidates |
|
1179
|
|
|
|
|
|
|
beat parent class candidates, they should mostly behave as expected. |
|
1180
|
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
After this, there should be one preferred candidate or none. If there is |
|
1182
|
|
|
|
|
|
|
none, an error occurs. If there is one, that candidate is dispatched to |
|
1183
|
|
|
|
|
|
|
using C<goto> so there is no trace of Sub::MultiMethod in C<caller>. It |
|
1184
|
|
|
|
|
|
|
gets passed the result from checking the signature earlier as C<< @_ >>. |
|
1185
|
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
=head3 Roles |
|
1187
|
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
As far as I'm aware, Sub::MultiMethod is the only multimethod implementation |
|
1189
|
|
|
|
|
|
|
that allows multimethods imported from roles to integrate into a class. |
|
1190
|
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
use v5.12; |
|
1192
|
|
|
|
|
|
|
use strict; |
|
1193
|
|
|
|
|
|
|
use warnings; |
|
1194
|
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
package My::RoleA { |
|
1196
|
|
|
|
|
|
|
use Moo::Role; |
|
1197
|
|
|
|
|
|
|
use Sub::MultiMethod qw(multimethod); |
|
1198
|
|
|
|
|
|
|
use Types::Standard -types; |
|
1199
|
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
multimethod foo => ( |
|
1201
|
|
|
|
|
|
|
positional => [ HashRef ], |
|
1202
|
|
|
|
|
|
|
code => sub { return "A" }, |
|
1203
|
|
|
|
|
|
|
alias => "foo_a", |
|
1204
|
|
|
|
|
|
|
); |
|
1205
|
|
|
|
|
|
|
} |
|
1206
|
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
package My::RoleB { |
|
1208
|
|
|
|
|
|
|
use Moo::Role; |
|
1209
|
|
|
|
|
|
|
use Sub::MultiMethod qw(multimethod); |
|
1210
|
|
|
|
|
|
|
use Types::Standard -types; |
|
1211
|
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
multimethod foo => ( |
|
1213
|
|
|
|
|
|
|
positional => [ ArrayRef ], |
|
1214
|
|
|
|
|
|
|
code => sub { return "B" }, |
|
1215
|
|
|
|
|
|
|
); |
|
1216
|
|
|
|
|
|
|
} |
|
1217
|
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
package My::Class { |
|
1219
|
|
|
|
|
|
|
use Moo; |
|
1220
|
|
|
|
|
|
|
use Sub::MultiMethod qw(multimethod); |
|
1221
|
|
|
|
|
|
|
use Types::Standard -types; |
|
1222
|
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
with qw( My::RoleA My::RoleB ); |
|
1224
|
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
multimethod foo => ( |
|
1226
|
|
|
|
|
|
|
positional => [ HashRef ], |
|
1227
|
|
|
|
|
|
|
code => sub { return "C" }, |
|
1228
|
|
|
|
|
|
|
); |
|
1229
|
|
|
|
|
|
|
} |
|
1230
|
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
my $obj = My::Class->new; |
|
1232
|
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
say $obj->foo_a( {} ); # A (alias defined in RoleA) |
|
1234
|
|
|
|
|
|
|
say $obj->foo( [] ); # B (candidate from RoleB) |
|
1235
|
|
|
|
|
|
|
say $obj->foo( {} ); # C (Class overrides candidate from RoleA) |
|
1236
|
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
All other things being equal, candidates defined in classes should |
|
1238
|
|
|
|
|
|
|
beat candidates imported from roles. |
|
1239
|
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
=head2 CodeRef multimethods |
|
1241
|
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
The C<< $name >> of a multimethod may be a scalarref, in which case |
|
1243
|
|
|
|
|
|
|
C<multimethod> will install the multimethod as a coderef into the |
|
1244
|
|
|
|
|
|
|
scalar referred to. Example: |
|
1245
|
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
my ($coderef, $otherref); |
|
1247
|
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
multimethod \$coderef => ( |
|
1249
|
|
|
|
|
|
|
method => 0, |
|
1250
|
|
|
|
|
|
|
positional => [ ArrayRef ], |
|
1251
|
|
|
|
|
|
|
code => sub { say "It's an arrayref!" }, |
|
1252
|
|
|
|
|
|
|
); |
|
1253
|
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
multimethod \$coderef => ( |
|
1255
|
|
|
|
|
|
|
method => 0, |
|
1256
|
|
|
|
|
|
|
alias => \$otherref, |
|
1257
|
|
|
|
|
|
|
positional => [ HashRef ], |
|
1258
|
|
|
|
|
|
|
code => sub { say "It's a hashref!" }, |
|
1259
|
|
|
|
|
|
|
); |
|
1260
|
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
$coderef->( [] ); |
|
1262
|
|
|
|
|
|
|
$coderef->( {} ); |
|
1263
|
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
$otherref->( {} ); |
|
1265
|
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
The C<< $coderef >> and C<< $otherref >> variables will actually end up |
|
1267
|
|
|
|
|
|
|
as blessed coderefs so that some tidy ups can take place in C<DESTROY>. |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
=head2 Exporter |
|
1270
|
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
Sub::MultiMethod uses L<Exporter::Tiny> as an exporter, which means |
|
1272
|
|
|
|
|
|
|
exported functions can be renamed, etc. |
|
1273
|
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
use Sub::MultiMethod multimethod => { -as => 'mm' }; |
|
1275
|
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
You can import everything using C<< -all >>: |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
use Sub::MultiMethod -all; |
|
1279
|
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
If your Perl version is recent enough, you can import everything as lexical |
|
1281
|
|
|
|
|
|
|
keywords: |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
use Sub::MultiMethod -lexical, -all; |
|
1284
|
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
You may also set various defaults in the import: |
|
1286
|
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
use Sub::MultiMethod multimethod => { |
|
1288
|
|
|
|
|
|
|
-as => 'mm', |
|
1289
|
|
|
|
|
|
|
defaults => { bless => 0 }, # see Type::Params |
|
1290
|
|
|
|
|
|
|
}; |
|
1291
|
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
=head2 API |
|
1293
|
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
Sub::MultiMethod avoids cute syntax hacks because those can be added by |
|
1295
|
|
|
|
|
|
|
third party modules. It provides an API for these modules. |
|
1296
|
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
Brief note on terminology: when you define multimethods in a class, |
|
1298
|
|
|
|
|
|
|
each possible signature+coderef is a "candidate". The method which |
|
1299
|
|
|
|
|
|
|
makes the decision about which candidate to call is the "dispatcher". |
|
1300
|
|
|
|
|
|
|
Roles will typically have candidates but no dispatcher. Classes will |
|
1301
|
|
|
|
|
|
|
need dispatchers setting up for each multimethod. |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
=over |
|
1304
|
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->install_candidate($target, $sub_name, %spec) >> |
|
1306
|
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
C<< $target >> is the class (package) name being installed into. |
|
1308
|
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
C<< $sub_name >> is the name of the method. |
|
1310
|
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
C<< %spec >> is the multimethod spec. If C<< $target >> is a role, you |
|
1312
|
|
|
|
|
|
|
probably want to include C<< no_dispatcher => 1 >> as part of the spec. |
|
1313
|
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->install_dispatcher($target, $sub_name, $is_method) >> |
|
1315
|
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
C<< $target >> is the class (package) name being installed into. |
|
1317
|
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
C<< $sub_name >> is the name of the method. |
|
1319
|
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
C<< $is_method >> is an integer/boolean. |
|
1321
|
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
This rarely needs to be manually called as C<install_candidate> will do it |
|
1323
|
|
|
|
|
|
|
automatically. |
|
1324
|
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->install_monomethod($target, $sub_name, %spec) >> |
|
1326
|
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
Installs a regular (non-multimethod) method into the target. |
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->copy_package_candidates(@sources => $target) >> |
|
1330
|
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
C<< @sources >> is the list of packages to copy candidates from. |
|
1332
|
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
C<< $target >> is the class (package) name being installed into. |
|
1334
|
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
Sub::MultiMethod will use L<Role::Hooks> to automatically copy candidates |
|
1336
|
|
|
|
|
|
|
from roles to consuming classes if your role implementation is supported. |
|
1337
|
|
|
|
|
|
|
(Supported implementations include Role::Tiny, Role::Basic, Moo::Role, |
|
1338
|
|
|
|
|
|
|
Moose::Role, and Mouse::Role, plus any role implementations that extend |
|
1339
|
|
|
|
|
|
|
those. If your role implementation is something else, then when you consume |
|
1340
|
|
|
|
|
|
|
a role into a class you may need to copy the candidates from the role to |
|
1341
|
|
|
|
|
|
|
the class.) |
|
1342
|
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->install_missing_dispatchers($target) >> |
|
1344
|
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
Should usually be called after C<copy_package_candidates>, unless |
|
1346
|
|
|
|
|
|
|
C<< $target >> is a role. |
|
1347
|
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
Again, this is unnecessary if your role implementation is supported |
|
1349
|
|
|
|
|
|
|
by Role::Hooks. |
|
1350
|
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->get_multimethods($target) >> |
|
1352
|
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
Returns the names of all multimethods declared for a class or role, |
|
1354
|
|
|
|
|
|
|
not including any parent classes. |
|
1355
|
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->has_multimethod_candidates($target, $method_name) >> |
|
1357
|
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
Indicates whether the class or role has any candidates for a multimethod. |
|
1359
|
|
|
|
|
|
|
Does not include parent classes. |
|
1360
|
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->get_multimethod_candidates($target, $method_name) >> |
|
1362
|
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
Returns a list of candidate spec hashrefs for the method, not including |
|
1364
|
|
|
|
|
|
|
candidates from parent classes. |
|
1365
|
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->get_all_multimethod_candidates($target, $method_name, $is_method) >> |
|
1367
|
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
Returns a list of candidate spec hashrefs for the method, including candidates |
|
1369
|
|
|
|
|
|
|
from parent classes (unless C<< $is_method >> is false, because non-methods |
|
1370
|
|
|
|
|
|
|
shouldn't be inherited). |
|
1371
|
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->known_dispatcher($coderef) >> |
|
1373
|
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
Returns a boolean indicating whether the coderef is known to be a multimethod |
|
1375
|
|
|
|
|
|
|
dispatcher. |
|
1376
|
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->pick_candidate(\@candidates, \@args, $wantarray) >> |
|
1378
|
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
Returns a list of two items: first the winning candidate from an array of specs, |
|
1380
|
|
|
|
|
|
|
given the args and invocants, and second the modified args after coercion has |
|
1381
|
|
|
|
|
|
|
been applied. C<< $wantarray >> should be a string 'VOID', 'SCALAR', or 'LIST'. |
|
1382
|
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
This is basically how the dispatcher for a method works: |
|
1384
|
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
my $pkg = __PACKAGE__; |
|
1386
|
|
|
|
|
|
|
if ( $ismethod ) { |
|
1387
|
|
|
|
|
|
|
$pkg = Scalar::Util::blessed( $_[0] ) || $_[0]; |
|
1388
|
|
|
|
|
|
|
} |
|
1389
|
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
my ( $winner, $new_args ) = 'Sub::MultiMethod'->pick_candidate( |
|
1391
|
|
|
|
|
|
|
[ |
|
1392
|
|
|
|
|
|
|
'Sub::MultiMethod'->get_all_multimethod_candidates( |
|
1393
|
|
|
|
|
|
|
$pkg, |
|
1394
|
|
|
|
|
|
|
$sub, |
|
1395
|
|
|
|
|
|
|
$ismethod, |
|
1396
|
|
|
|
|
|
|
) |
|
1397
|
|
|
|
|
|
|
], |
|
1398
|
|
|
|
|
|
|
\@_, |
|
1399
|
|
|
|
|
|
|
wantarray ? 'LIST' : defined(wantarray) ? 'SCALAR' : 'VOID', |
|
1400
|
|
|
|
|
|
|
); |
|
1401
|
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
$winner->{code}->( @$new_args ); |
|
1403
|
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->clear_cache >> |
|
1405
|
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
The C<dispatch> method caches what C<get_all_multimethod_candidates> returns. |
|
1407
|
|
|
|
|
|
|
It is expected that by the time a multisub/multimethod is called, you have |
|
1408
|
|
|
|
|
|
|
finished adding new candidates, so this should not be harmful. If you do add |
|
1409
|
|
|
|
|
|
|
new candidates, then the cache should automatically clear itself anyway. |
|
1410
|
|
|
|
|
|
|
However if new candidates emerge by, for example, altering a class's |
|
1411
|
|
|
|
|
|
|
C<< @ISA >> at run time, you may need to manually clear the cache. This is |
|
1412
|
|
|
|
|
|
|
a very unlikely situation though. |
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
=item C<< Sub::MultiMethod->get_cache >> |
|
1415
|
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
Gets a reference to the dispatch cache hash. Mostly for people wanting to |
|
1417
|
|
|
|
|
|
|
subclass Sub::MultiMethod, especially if you want to override the C<dispatch> |
|
1418
|
|
|
|
|
|
|
method. |
|
1419
|
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
=back |
|
1421
|
|
|
|
|
|
|
|
|
1422
|
|
|
|
|
|
|
=head1 EXAMPLES |
|
1423
|
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
=head2 Naive (Slightly Broken) JSON Writer |
|
1425
|
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
Here is similar code to the L</SYNOPSIS>, but written as a function |
|
1427
|
|
|
|
|
|
|
instead of a method, employing a few shortcuts, and with a variant |
|
1428
|
|
|
|
|
|
|
that throws an error if called in void context. |
|
1429
|
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
use v5.20; |
|
1431
|
|
|
|
|
|
|
use strict; |
|
1432
|
|
|
|
|
|
|
use warnings; |
|
1433
|
|
|
|
|
|
|
use experimental 'signatures'; |
|
1434
|
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
package My::JSON { |
|
1436
|
|
|
|
|
|
|
use Sub::MultiMethod multifunction => { -as => 'multi' }; |
|
1437
|
|
|
|
|
|
|
use Types::Standard -types; |
|
1438
|
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
multi stringify => ( want => 'VOID', die => 'Unexpected void context' ); |
|
1440
|
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
multi stringify => [ Undef ] => ( return => 'null' ); |
|
1442
|
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
multi stringify => [ ScalarRef[Bool] ] => sub ( $bool ) { |
|
1444
|
|
|
|
|
|
|
return $$bool ? 'true' : 'false'; |
|
1445
|
|
|
|
|
|
|
}; |
|
1446
|
|
|
|
|
|
|
|
|
1447
|
|
|
|
|
|
|
multi stringify => [ Str ] => ( |
|
1448
|
|
|
|
|
|
|
alias => "stringify_str", |
|
1449
|
|
|
|
|
|
|
code => sub ( $str ) { |
|
1450
|
|
|
|
|
|
|
return sprintf( q<"%s">, quotemeta($str) ); |
|
1451
|
|
|
|
|
|
|
}, |
|
1452
|
|
|
|
|
|
|
); |
|
1453
|
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
multi stringify => [ Num ] => sub ( $n ) { |
|
1455
|
|
|
|
|
|
|
return $n; |
|
1456
|
|
|
|
|
|
|
}; |
|
1457
|
|
|
|
|
|
|
|
|
1458
|
|
|
|
|
|
|
multi stringify => [ ArrayRef ] => sub ( $arr ) { |
|
1459
|
|
|
|
|
|
|
return sprintf( |
|
1460
|
|
|
|
|
|
|
q<[%s]>, |
|
1461
|
|
|
|
|
|
|
join( q<,>, map( stringify($_), @$arr ) ) |
|
1462
|
|
|
|
|
|
|
); |
|
1463
|
|
|
|
|
|
|
}; |
|
1464
|
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
multi stringify => [ HashRef ] => sub ( $hash ) { |
|
1466
|
|
|
|
|
|
|
return sprintf( |
|
1467
|
|
|
|
|
|
|
q<{%s}>, |
|
1468
|
|
|
|
|
|
|
join( |
|
1469
|
|
|
|
|
|
|
q<,>, |
|
1470
|
|
|
|
|
|
|
map sprintf( |
|
1471
|
|
|
|
|
|
|
q<%s:%s>, |
|
1472
|
|
|
|
|
|
|
stringify_str($_), |
|
1473
|
|
|
|
|
|
|
stringify( $hash->{$_} ) |
|
1474
|
|
|
|
|
|
|
), sort keys %$hash, |
|
1475
|
|
|
|
|
|
|
) |
|
1476
|
|
|
|
|
|
|
); |
|
1477
|
|
|
|
|
|
|
}; |
|
1478
|
|
|
|
|
|
|
} |
|
1479
|
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
say My::JSON::stringify( { |
|
1481
|
|
|
|
|
|
|
foo => 123, |
|
1482
|
|
|
|
|
|
|
bar => [ 1, 2, 3 ], |
|
1483
|
|
|
|
|
|
|
baz => \1, |
|
1484
|
|
|
|
|
|
|
quux => { xyzzy => 666 }, |
|
1485
|
|
|
|
|
|
|
} ); |
|
1486
|
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
=head1 BUGS |
|
1488
|
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
Please report any bugs to |
|
1490
|
|
|
|
|
|
|
L<http://rt.cpan.org/Dist/Display.html?Queue=Sub-MultiMethod>. |
|
1491
|
|
|
|
|
|
|
|
|
1492
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
1493
|
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
L<Multi::Dispatch> - probably almost as nice an implementation as |
|
1495
|
|
|
|
|
|
|
Sub::MultiMethod. It correctly handles inheritance, does a good job of |
|
1496
|
|
|
|
|
|
|
dispatching to the best candidate, etc. It's even significantly faster than |
|
1497
|
|
|
|
|
|
|
Sub::MultiMethod. On the downsides, it doesn't handle roles or coercions. |
|
1498
|
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
L<Class::Multimethods> - uses Perl classes and ref types to dispatch. |
|
1500
|
|
|
|
|
|
|
No syntax hacks but the fairly nice syntax shown in the pod relies on |
|
1501
|
|
|
|
|
|
|
C<use strict> being switched off! Need to quote a few more things otherwise. |
|
1502
|
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
L<Class::Multimethods::Pure> - similar to Class::Multimethods but with |
|
1504
|
|
|
|
|
|
|
a more complex type system and a more complex dispatch method. |
|
1505
|
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
L<Logic> - a full declarative programming framework. Overkill if all |
|
1507
|
|
|
|
|
|
|
you want is multimethods. Uses source filters. |
|
1508
|
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
L<Dios> - object oriented programming framework including multimethods. |
|
1510
|
|
|
|
|
|
|
Includes a full type system and Keyword::Declare-based syntax. Pretty |
|
1511
|
|
|
|
|
|
|
sensible dispatch technique which is almost identical to |
|
1512
|
|
|
|
|
|
|
Sub::MultiMethod. Much much slower though, at both compile time and |
|
1513
|
|
|
|
|
|
|
runtime. |
|
1514
|
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
L<MooseX::MultiMethods> - uses Moose type system and Devel::Declare-based |
|
1516
|
|
|
|
|
|
|
syntax. Not entirely sure what the dispatching method is. |
|
1517
|
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
L<Kavorka> - I wrote this, so I'm allowed to be critical. Type::Tiny-based |
|
1519
|
|
|
|
|
|
|
type system. Very naive dispatching; just dispatches to the first declared |
|
1520
|
|
|
|
|
|
|
candidate that can handle it rather than trying to find the "best". |
|
1521
|
|
|
|
|
|
|
|
|
1522
|
|
|
|
|
|
|
L<Sub::Multi::Tiny> - uses Perl attributes to declare candidates to |
|
1523
|
|
|
|
|
|
|
be dispatched to. Pluggable dispatching, but by default uses argument |
|
1524
|
|
|
|
|
|
|
count. |
|
1525
|
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
L<Sub::Multi> - syntax wrapper around Class::Multimethods::Pure? |
|
1527
|
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
L<Sub::SmartMatch> - kind of abandoned and smartmatch is generally seen |
|
1529
|
|
|
|
|
|
|
as teh evilz these days. |
|
1530
|
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1532
|
|
|
|
|
|
|
|
|
1533
|
|
|
|
|
|
|
Toby Inkster E<lt>tobyink@cpan.orgE<gt>. |
|
1534
|
|
|
|
|
|
|
|
|
1535
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENCE |
|
1536
|
|
|
|
|
|
|
|
|
1537
|
|
|
|
|
|
|
This software is copyright (c) 2020-2022 by Toby Inkster. |
|
1538
|
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
1540
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
1541
|
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
=head1 DISCLAIMER OF WARRANTIES |
|
1543
|
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED |
|
1545
|
|
|
|
|
|
|
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
|
1546
|
|
|
|
|
|
|
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
|
1547
|
|
|
|
|
|
|
|