line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Symbol::Opaque; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
4
|
|
|
|
|
|
|
|
5
|
1
|
|
|
1
|
|
24150
|
use 5.006001; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
38
|
|
6
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
373
|
|
7
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
54
|
|
8
|
1
|
|
|
1
|
|
5
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
44
|
|
9
|
1
|
|
|
1
|
|
1583
|
use Class::Multimethods::Pure; |
|
1
|
|
|
|
|
20084
|
|
|
1
|
|
|
|
|
7
|
|
10
|
1
|
|
|
1
|
|
89
|
use Exporter; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
11
|
1
|
|
|
1
|
|
5
|
use Scalar::Util qw; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
48
|
|
12
|
1
|
|
|
1
|
|
5
|
use base 'Exporter'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
337
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our @EXPORT = qw; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub _() { |
17
|
1
|
|
|
1
|
|
7
|
Symbol::Opaque::Anything->new; |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
sub free($) { |
21
|
18
|
|
|
18
|
0
|
1346
|
Symbol::Opaque::Free->new(\$_[0]); |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub id($) { |
25
|
0
|
|
|
0
|
0
|
0
|
Symbol::Opaque::Id->new($_[0]); |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub makesymdef { |
29
|
2
|
|
|
2
|
0
|
3
|
my ($name) = @_; |
30
|
|
|
|
|
|
|
sub { |
31
|
39
|
|
|
39
|
|
2835
|
my @args; |
32
|
39
|
|
|
|
|
82
|
for my $i (0..$#_) { |
33
|
53
|
100
|
66
|
|
|
180
|
if (!defined $_[$i] && !readonly $_[$i]) { |
34
|
14
|
|
|
|
|
29
|
push @args, free $_[$i]; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
else { |
37
|
39
|
|
|
|
|
106
|
push @args, $_[$i]; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
} |
40
|
39
|
|
|
|
|
114
|
Symbol::Opaque::Symbol->new($name, @args); |
41
|
2
|
|
|
|
|
11
|
}; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub defsym { |
45
|
2
|
|
|
2
|
0
|
11
|
my ($name) = @_; |
46
|
1
|
|
|
1
|
|
6
|
no strict 'refs'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
652
|
|
47
|
2
|
|
|
|
|
4
|
my $package = caller; |
48
|
2
|
|
|
|
|
6
|
*{"$package\::$name"} = makesymdef $name; |
|
2
|
|
|
|
|
11
|
|
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
multi UNIFY => (Any, Any) => sub { |
52
|
|
|
|
|
|
|
my ($a, $b) = @_; |
53
|
|
|
|
|
|
|
$a eq $b and sub { }; |
54
|
|
|
|
|
|
|
}; |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
multi UNIFY => ('Symbol::Opaque::Free', Any) => sub { |
57
|
|
|
|
|
|
|
my ($var, $thing) = @_; |
58
|
|
|
|
|
|
|
$var->bind($thing); |
59
|
|
|
|
|
|
|
}; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
multi UNIFY => (subtype('Symbol::Opaque::Free', sub { $_[0]->bound }), Any) => sub { |
62
|
|
|
|
|
|
|
my ($var, $thing) = @_; |
63
|
|
|
|
|
|
|
UNIFY($var->value, $thing); |
64
|
|
|
|
|
|
|
}; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
multi UNIFY => ('Symbol::Opaque::Symbol', Any) => sub { |
67
|
|
|
|
|
|
|
0; |
68
|
|
|
|
|
|
|
}; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
multi UNIFY => ('Symbol::Opaque::Symbol', 'Symbol::Opaque::Symbol') => sub { |
71
|
|
|
|
|
|
|
my ($sa, $sb) = @_; |
72
|
|
|
|
|
|
|
return 0 unless $sa->name eq $sb->name; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
UNIFY([$sa->args], [$sb->args]); |
75
|
|
|
|
|
|
|
}; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
multi UNIFY => ('Symbol::Opaque::Anything', Any) => sub { |
78
|
|
|
|
|
|
|
sub { }; |
79
|
|
|
|
|
|
|
}; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
multi UNIFY => ('ARRAY', 'ARRAY') => sub { |
82
|
|
|
|
|
|
|
my ($a, $b) = @_; |
83
|
|
|
|
|
|
|
return 0 unless @$a == @$b; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my @rollback; |
86
|
|
|
|
|
|
|
for my $i (0..$#$a) { |
87
|
|
|
|
|
|
|
my $code = UNIFY($a->[$i], $b->[$i]); |
88
|
|
|
|
|
|
|
if ($code) { |
89
|
|
|
|
|
|
|
push @rollback, $code; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
else { |
92
|
|
|
|
|
|
|
$_->() for @rollback; |
93
|
|
|
|
|
|
|
return 0; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
return sub { $_->() for @rollback }; |
98
|
|
|
|
|
|
|
}; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Hash-hash unification is a little subtle. |
101
|
|
|
|
|
|
|
# The right hash has to have every key-value pair as the left hash, |
102
|
|
|
|
|
|
|
# but the right may have extra keys and that's okay. |
103
|
|
|
|
|
|
|
multi UNIFY => ('HASH', 'HASH') => sub { |
104
|
|
|
|
|
|
|
my ($a, $b) = @_; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
my @keys = keys %$a; |
107
|
|
|
|
|
|
|
for (@keys) { |
108
|
|
|
|
|
|
|
return 0 unless exists $b->{$_}; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
UNIFY([ @$a{@keys} ], [ @$b{@keys} ]); |
111
|
|
|
|
|
|
|
}; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
package Symbol::Opaque::Ops; |
114
|
|
|
|
|
|
|
|
115
|
1
|
|
|
1
|
|
13
|
use Class::Multimethods::Pure multi => 'UNIFY'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
use overload |
118
|
15
|
|
|
15
|
|
53
|
'<<' => sub { ! !UNIFY($_[0], $_[1]) }, |
119
|
0
|
|
|
0
|
|
0
|
'>>' => sub { ! !UNIFY($_[1], $_[0]) }, |
120
|
0
|
|
|
0
|
|
0
|
'""' => sub { overload::StrVal($_[0]) }, |
121
|
1
|
|
|
1
|
|
2294
|
; |
|
1
|
|
|
|
|
1324
|
|
|
1
|
|
|
|
|
14
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
package Symbol::Opaque::Symbol; |
124
|
|
|
|
|
|
|
|
125
|
1
|
|
|
1
|
|
101
|
use base 'Symbol::Opaque::Ops'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
702
|
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub new { |
128
|
39
|
|
|
39
|
|
71
|
my ($class, $name, @args) = @_; |
129
|
39
|
|
33
|
|
|
404
|
bless { |
130
|
|
|
|
|
|
|
name => $name, |
131
|
|
|
|
|
|
|
args => \@args, |
132
|
|
|
|
|
|
|
} => ref $class || $class; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub name { |
136
|
38
|
|
|
38
|
|
47
|
my ($self) = @_; |
137
|
38
|
|
|
|
|
102
|
$self->{name}; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub args { |
141
|
38
|
|
|
38
|
|
40
|
my ($self) = @_; |
142
|
38
|
|
|
|
|
34
|
@{$self->{args}}; |
|
38
|
|
|
|
|
134
|
|
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
package Symbol::Opaque::Free; |
146
|
|
|
|
|
|
|
|
147
|
1
|
|
|
1
|
|
11
|
use base 'Symbol::Opaque::Ops'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
593
|
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub new { |
150
|
18
|
|
|
18
|
|
32
|
my ($class, $ref) = @_; |
151
|
18
|
|
|
|
|
50
|
undef $$ref; |
152
|
18
|
|
33
|
|
|
161
|
bless { |
153
|
|
|
|
|
|
|
ref => $ref, |
154
|
|
|
|
|
|
|
} => ref $class || $class; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub bind { |
158
|
11
|
|
|
11
|
|
13
|
my ($self, $thing) = @_; |
159
|
11
|
|
|
|
|
13
|
${$self->{ref}} = $thing; |
|
11
|
|
|
|
|
18
|
|
160
|
|
|
|
|
|
|
sub { |
161
|
3
|
|
|
3
|
|
4
|
undef ${$self->{ref}}; |
|
3
|
|
|
|
|
10
|
|
162
|
11
|
|
|
|
|
52
|
}; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub bound { |
166
|
15
|
|
|
15
|
|
25
|
my ($self) = @_; |
167
|
15
|
|
|
|
|
15
|
defined ${$self->{ref}}; |
|
15
|
|
|
|
|
105
|
|
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub value { |
171
|
4
|
|
|
4
|
|
7
|
my ($self) = @_; |
172
|
4
|
|
|
|
|
6
|
${$self->{ref}}; |
|
4
|
|
|
|
|
15
|
|
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
package Symbol::Opaque::Anything; |
176
|
|
|
|
|
|
|
|
177
|
1
|
|
|
1
|
|
19
|
use base 'Symbol::Opaque::Ops'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
524
|
|
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub new { |
180
|
1
|
|
|
1
|
|
3
|
my ($class) = @_; |
181
|
1
|
|
33
|
|
|
9
|
bless {} => ref $class || $class; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
1; |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=head1 NAME |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
Symbol::Opaque - ML-ish data constructor pattern matching |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 SYNOPSIS |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
use Symbol::Opaque; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
BEGIN { |
195
|
|
|
|
|
|
|
defsym('foo'); # define the constructor "foo" |
196
|
|
|
|
|
|
|
defsym('bar'); # define the constructor "bar" |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
if ( foo(my $x) << foo(4) ) { # bind foo(4) into foo($x) |
200
|
|
|
|
|
|
|
# $x is now 4 |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
if ( foo(13, bar(my $x)) << foo(13, bar("baz")) ) { |
204
|
|
|
|
|
|
|
# $x is now "baz" |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
if ( foo(my $x) << bar(42) ) { |
208
|
|
|
|
|
|
|
# not executed: foo(X) doesn't match bar(42) |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head1 DESCRIPTION |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
This module allows the creation of data constructors, which can then be |
214
|
|
|
|
|
|
|
conditionally unified like in Haskell or ML. When you use the B |
215
|
|
|
|
|
|
|
operator C<<< << >>>, between two structures, this module tries to bind any |
216
|
|
|
|
|
|
|
I on the left in order to make the structures the same. |
217
|
|
|
|
|
|
|
For example: |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
foo(my $x) << foo(14) # true, $x becomes 14 |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
This will make $x equal 14, and then the operator will return true. Sometimes |
222
|
|
|
|
|
|
|
it is impossible to make them the same, and in that case no variables are |
223
|
|
|
|
|
|
|
changed and the operator returns false. For instance: |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
foo(my $x, 10) << foo(20, 21) # impossible: false, $x is undef |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
This makes it possible to write cascades of tests on a value: |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
my $y = foo(20, 21); |
230
|
|
|
|
|
|
|
if (foo("hello", my $x) << $y) { |
231
|
|
|
|
|
|
|
... |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
elsif (foo(my $x, 21) << $y) { |
234
|
|
|
|
|
|
|
# this gets executed: $x is 20 |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
else { |
237
|
|
|
|
|
|
|
die "No match"; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
(Yes, Perl lets you declare the same variable twice in the same cascade -- just |
241
|
|
|
|
|
|
|
not in the same condition). |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Before you can do this, though, you have to tell Perl that C is such a |
244
|
|
|
|
|
|
|
data constructor. This is done with the exported C routine. It is |
245
|
|
|
|
|
|
|
advisable that you do this in a C block, so that the execution path |
246
|
|
|
|
|
|
|
doesn't have to reach it for it to be defined: |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
BEGIN { |
249
|
|
|
|
|
|
|
defsym('foo'); # foo() is a data constructor |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
If two different modules both declare a 'foo' symbol, I
|
253
|
|
|
|
|
|
|
same>. The reason this isn't dangerous is because the only thing that can ever |
254
|
|
|
|
|
|
|
differ about two symbols is their name: there is no "implementation" defined. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
The unification performed is I: you can only have free |
257
|
|
|
|
|
|
|
variables on the left side. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
The unification performed is I: you can mention the same free |
260
|
|
|
|
|
|
|
variable more than once: |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
my $x; # we must declare first when there is more than one mention |
263
|
|
|
|
|
|
|
foo($x, $x) << foo(4, 4); # true; $x = 4 |
264
|
|
|
|
|
|
|
foo($x, $x) << foo(4, 5); # false |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
Unification of arrays is performed by comparing them elementwise, just like the |
267
|
|
|
|
|
|
|
arguments of a structure. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Unification of hashes is done like so: Every key that the target (left) hash |
270
|
|
|
|
|
|
|
has, the source (right) hash must also, and their values must unify. However, |
271
|
|
|
|
|
|
|
the source hash may have keys that the target hash does not, and the two hashes |
272
|
|
|
|
|
|
|
will still unify. This is so you can support "property lists", and unify |
273
|
|
|
|
|
|
|
against structures that have certain properties. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
A variable is considered free if it is writable (this is true of all variables |
276
|
|
|
|
|
|
|
that you'll pass in), undefined, and in the top level of a constructor. That |
277
|
|
|
|
|
|
|
is: |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
foo([1, my $x]) << foo([1,2]) |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Will not unify $x, since it is not directly in a data constructor. To get |
282
|
|
|
|
|
|
|
around this, you can explicitly mark variables as free with the C |
283
|
|
|
|
|
|
|
function: |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
foo([1, free my $x]) << foo([1,2]) # success: $x == 2 |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
Sometimes you have a situation where you're unifying against a structure, |
288
|
|
|
|
|
|
|
and you want something to be in a position, but you don't care what it is. |
289
|
|
|
|
|
|
|
The C<_> marker is used in this case: |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
foo([1, _]) << foo([1, 2]) # success: no bindings |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head1 SEE ALSO |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
L |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head1 AUTHOR |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
Luke Palmer |