line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# ABSTRACT: like Type::Tie, but slower and more flexible |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
###################################################################### |
4
|
|
|
|
|
|
|
# Copyright (C) 2021 Asher Gordon # |
5
|
|
|
|
|
|
|
# # |
6
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or # |
7
|
|
|
|
|
|
|
# modify it under the terms of the GNU General Public License as # |
8
|
|
|
|
|
|
|
# published by the Free Software Foundation, either version 3 of # |
9
|
|
|
|
|
|
|
# the License, or (at your option) any later version. # |
10
|
|
|
|
|
|
|
# # |
11
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, # |
12
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of # |
13
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # |
14
|
|
|
|
|
|
|
# General Public License for more details. # |
15
|
|
|
|
|
|
|
# # |
16
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License # |
17
|
|
|
|
|
|
|
# along with this program. If not, see # |
18
|
|
|
|
|
|
|
# . # |
19
|
|
|
|
|
|
|
###################################################################### |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
package Type::Tie::Aggregate; |
22
|
|
|
|
|
|
|
$Type::Tie::Aggregate::VERSION = '0.001'; |
23
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
24
|
|
|
|
|
|
|
#pod |
25
|
|
|
|
|
|
|
#pod use Type::Tie::Aggregate; |
26
|
|
|
|
|
|
|
#pod use Types::Standard qw(Dict Optional Num Str); |
27
|
|
|
|
|
|
|
#pod |
28
|
|
|
|
|
|
|
#pod ttie my %hash, Dict[name => Str, age => Optional[Num]], ( |
29
|
|
|
|
|
|
|
#pod name => 'John Doe', |
30
|
|
|
|
|
|
|
#pod age => 42, |
31
|
|
|
|
|
|
|
#pod ); |
32
|
|
|
|
|
|
|
#pod |
33
|
|
|
|
|
|
|
#pod $hash{name} = 'Jane Doe'; # ok |
34
|
|
|
|
|
|
|
#pod $hash{age}++; # ok |
35
|
|
|
|
|
|
|
#pod $hash{age} = 'forty-two; # dies |
36
|
|
|
|
|
|
|
#pod delete $hash{name}; # dies ('name' is mandatory) |
37
|
|
|
|
|
|
|
#pod |
38
|
|
|
|
|
|
|
#pod # Unfortunately this does not work, because the hash is |
39
|
|
|
|
|
|
|
#pod # momentarily cleared and will no longer pass the type constraint |
40
|
|
|
|
|
|
|
#pod # (which requires a 'name' key). |
41
|
|
|
|
|
|
|
#pod %hash = (name => 'J. Random Hacker'); |
42
|
|
|
|
|
|
|
#pod |
43
|
|
|
|
|
|
|
#pod # Use this instead (also more efficient). |
44
|
|
|
|
|
|
|
#pod (tied %hash)->initialize(name => 'J. Random Hacker'); |
45
|
|
|
|
|
|
|
#pod |
46
|
|
|
|
|
|
|
#pod =head1 DESCRIPTION |
47
|
|
|
|
|
|
|
#pod |
48
|
|
|
|
|
|
|
#pod Like L, this module exports a single function: |
49
|
|
|
|
|
|
|
#pod C. Also like L, C ties a variable to |
50
|
|
|
|
|
|
|
#pod a type constraint (coercions will be honored). |
51
|
|
|
|
|
|
|
#pod |
52
|
|
|
|
|
|
|
#pod However, unlike L, when an assignment happens on |
53
|
|
|
|
|
|
|
#pod a variable tied with C, the I variable will be |
54
|
|
|
|
|
|
|
#pod re-checked, not just the value that was added. This is much more |
55
|
|
|
|
|
|
|
#pod expensive, of course, but can be very useful for structured types such |
56
|
|
|
|
|
|
|
#pod as C from L as show in the |
57
|
|
|
|
|
|
|
#pod L. |
58
|
|
|
|
|
|
|
#pod |
59
|
|
|
|
|
|
|
#pod Any type constraints supporting the L interface |
60
|
|
|
|
|
|
|
#pod should work, not just L types. However, in the |
61
|
|
|
|
|
|
|
#pod examples that follow, all type constraints are from |
62
|
|
|
|
|
|
|
#pod L unless specified otherwise. |
63
|
|
|
|
|
|
|
#pod |
64
|
|
|
|
|
|
|
#pod =head2 Initialization and Re-initialization |
65
|
|
|
|
|
|
|
#pod |
66
|
|
|
|
|
|
|
#pod Since some types don't allow empty values (see the L), |
67
|
|
|
|
|
|
|
#pod values may need to be given when initializing the type. For example, |
68
|
|
|
|
|
|
|
#pod this is invalid: |
69
|
|
|
|
|
|
|
#pod |
70
|
|
|
|
|
|
|
#pod ttie my %hash, Dict[name => Str]; # dies |
71
|
|
|
|
|
|
|
#pod |
72
|
|
|
|
|
|
|
#pod No values were given to initialize C<%hash>, so C<%hash> failed the |
73
|
|
|
|
|
|
|
#pod type constraint C Str]> (which requires a C |
74
|
|
|
|
|
|
|
#pod key). Instead, this should be done: |
75
|
|
|
|
|
|
|
#pod |
76
|
|
|
|
|
|
|
#pod ttie my %hash, Dict[name => Str], (name => 'My Name'); |
77
|
|
|
|
|
|
|
#pod |
78
|
|
|
|
|
|
|
#pod This initializes C<%hash> with the value C<< (name => 'My Name') >> |
79
|
|
|
|
|
|
|
#pod before any type checking is performed, so, at the end of the day, |
80
|
|
|
|
|
|
|
#pod C<%hash> passes the type constraint. |
81
|
|
|
|
|
|
|
#pod |
82
|
|
|
|
|
|
|
#pod Another important thing to note is that when a variable is |
83
|
|
|
|
|
|
|
#pod re-initialized, it is temporarily emptied. So the following is |
84
|
|
|
|
|
|
|
#pod invalid: |
85
|
|
|
|
|
|
|
#pod |
86
|
|
|
|
|
|
|
#pod ttie my %hash, Dict[name => Str], (name => 'My Name'); |
87
|
|
|
|
|
|
|
#pod %hash = (name => 'Other Name'); # dies |
88
|
|
|
|
|
|
|
#pod |
89
|
|
|
|
|
|
|
#pod Instead, the C method should be used on the tied object, |
90
|
|
|
|
|
|
|
#pod like so: |
91
|
|
|
|
|
|
|
#pod |
92
|
|
|
|
|
|
|
#pod ttie my %hash, Dict[name => Str], (name => 'My Name'); |
93
|
|
|
|
|
|
|
#pod (tied %hash)->initialize(name => 'Other Name'); # ok |
94
|
|
|
|
|
|
|
#pod |
95
|
|
|
|
|
|
|
#pod This is also more efficient than the previous method. |
96
|
|
|
|
|
|
|
#pod |
97
|
|
|
|
|
|
|
#pod =head2 Deep Tying |
98
|
|
|
|
|
|
|
#pod |
99
|
|
|
|
|
|
|
#pod C ties variables deeply, meaning that if any references |
100
|
|
|
|
|
|
|
#pod contained within the variable are changed, the entire variable is |
101
|
|
|
|
|
|
|
#pod rechecked against the type constraint. Blessed objects are not deeply |
102
|
|
|
|
|
|
|
#pod tied, but tied references are and the functionality of these tied |
103
|
|
|
|
|
|
|
#pod references is preserved. |
104
|
|
|
|
|
|
|
#pod |
105
|
|
|
|
|
|
|
#pod For example, the following Does The Right Thing(TM): |
106
|
|
|
|
|
|
|
#pod |
107
|
|
|
|
|
|
|
#pod ttie my %hash, HashRef[ArrayRef[Int]]; |
108
|
|
|
|
|
|
|
#pod $hash{foo} = [1, 2, 3]; # ok |
109
|
|
|
|
|
|
|
#pod $hash{foo}[0] = 'one'; # dies |
110
|
|
|
|
|
|
|
#pod $hash{bar} = [3, 2, 1]; # ok |
111
|
|
|
|
|
|
|
#pod push @{$hash{bar}}, 'zero'; # dies |
112
|
|
|
|
|
|
|
#pod |
113
|
|
|
|
|
|
|
#pod This also works: |
114
|
|
|
|
|
|
|
#pod |
115
|
|
|
|
|
|
|
#pod use List::Util qw(all); |
116
|
|
|
|
|
|
|
#pod use Tie::RefHash; |
117
|
|
|
|
|
|
|
#pod |
118
|
|
|
|
|
|
|
#pod ttie my @array, ArrayRef[HashRef[Int]]; |
119
|
|
|
|
|
|
|
#pod |
120
|
|
|
|
|
|
|
#pod my $scalar_key = 'scalar'; |
121
|
|
|
|
|
|
|
#pod my @array_key = (1, 2, 3); |
122
|
|
|
|
|
|
|
#pod tie my %refhash, 'Tie::RefHash', ( |
123
|
|
|
|
|
|
|
#pod \$scalar_key => 1, |
124
|
|
|
|
|
|
|
#pod \@array_key => 2, |
125
|
|
|
|
|
|
|
#pod ); |
126
|
|
|
|
|
|
|
#pod |
127
|
|
|
|
|
|
|
#pod push @array, \%refhash; |
128
|
|
|
|
|
|
|
#pod |
129
|
|
|
|
|
|
|
#pod $array[0]{\$scalar_key} = 'foo'; # dies |
130
|
|
|
|
|
|
|
#pod $array[0]{\@array_key} = 42; # ok |
131
|
|
|
|
|
|
|
#pod all { ref ne '' } keys %{$array[0]}; # true |
132
|
|
|
|
|
|
|
#pod |
133
|
|
|
|
|
|
|
#pod Currently, circular references are not handled correctly (see |
134
|
|
|
|
|
|
|
#pod L). |
135
|
|
|
|
|
|
|
#pod |
136
|
|
|
|
|
|
|
#pod =head1 CAVEATS |
137
|
|
|
|
|
|
|
#pod |
138
|
|
|
|
|
|
|
#pod =head2 Re-initialization |
139
|
|
|
|
|
|
|
#pod |
140
|
|
|
|
|
|
|
#pod Re-initialization of tied variables using C<@array = @init> or |
141
|
|
|
|
|
|
|
#pod C<%hash = %init> does not always work. Use |
142
|
|
|
|
|
|
|
#pod C<< (tied @array)->initialize(@init) >> and |
143
|
|
|
|
|
|
|
#pod C<< (tied %hash)->initialize(%init) >> instead. See |
144
|
|
|
|
|
|
|
#pod L for more information. |
145
|
|
|
|
|
|
|
#pod |
146
|
|
|
|
|
|
|
#pod =head2 Retying References |
147
|
|
|
|
|
|
|
#pod |
148
|
|
|
|
|
|
|
#pod If a variable tied to a type contains a reference, then that reference |
149
|
|
|
|
|
|
|
#pod cannot be contained by any other variable tied to a type. For example, |
150
|
|
|
|
|
|
|
#pod the following will die: |
151
|
|
|
|
|
|
|
#pod |
152
|
|
|
|
|
|
|
#pod my $arrayref = [42]; |
153
|
|
|
|
|
|
|
#pod ttie my @num_array, ArrayRef[ArrayRef[Num]], ($arrayref); |
154
|
|
|
|
|
|
|
#pod ttie my @str_array, ArrayRef[ArrayRef[Str]], ($arrayref); |
155
|
|
|
|
|
|
|
#pod |
156
|
|
|
|
|
|
|
#pod If this were allowed, it would not be clear whether |
157
|
|
|
|
|
|
|
#pod C should die or not. This behavior may be |
158
|
|
|
|
|
|
|
#pod changed in a later release, but you probably should not be doing this |
159
|
|
|
|
|
|
|
#pod regardless. |
160
|
|
|
|
|
|
|
#pod |
161
|
|
|
|
|
|
|
#pod =head2 Circular References |
162
|
|
|
|
|
|
|
#pod |
163
|
|
|
|
|
|
|
#pod Circular references are not handled correctly. Hopefully this will be |
164
|
|
|
|
|
|
|
#pod fixed in a future release. |
165
|
|
|
|
|
|
|
#pod |
166
|
|
|
|
|
|
|
#pod =cut |
167
|
|
|
|
|
|
|
|
168
|
5
|
|
|
5
|
|
435027
|
use v5.13.2; |
|
5
|
|
|
|
|
38
|
|
169
|
5
|
|
|
5
|
|
30
|
use strict; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
119
|
|
170
|
5
|
|
|
5
|
|
39
|
use warnings; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
153
|
|
171
|
5
|
|
|
5
|
|
2499
|
use namespace::autoclean; |
|
5
|
|
|
|
|
94270
|
|
|
5
|
|
|
|
|
24
|
|
172
|
5
|
|
|
5
|
|
354
|
use Carp; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
325
|
|
173
|
5
|
|
|
5
|
|
34
|
use Scalar::Util qw(reftype); |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
228
|
|
174
|
5
|
|
|
5
|
|
2817
|
use parent 'Exporter'; |
|
5
|
|
|
|
|
1631
|
|
|
5
|
|
|
|
|
31
|
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
our @EXPORT = qw(ttie); |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Used by Type::Tie::Aggregate::Deep; |
179
|
10
|
|
|
10
|
|
45
|
sub _tied_types { qw(SCALAR ARRAY HASH) } |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
{ |
182
|
|
|
|
|
|
|
my %tied_types = map { $_ => 1 } _tied_types; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# Used also by Type::Tie::Aggregate::Deep. Returns the type of the |
185
|
|
|
|
|
|
|
# reference. |
186
|
|
|
|
|
|
|
sub _check_ref_type { |
187
|
245
|
|
|
245
|
|
528
|
my ($class, $ref) = @_; |
188
|
245
|
|
33
|
|
|
732
|
my $type = reftype $ref // croak 'Not a reference'; |
189
|
245
|
50
|
|
|
|
622
|
$type = 'SCALAR' if $type eq 'REF'; |
190
|
245
|
50
|
|
|
|
604
|
return unless $tied_types{$type}; |
191
|
245
|
|
|
|
|
848
|
return $type; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
#pod =func ttie |
196
|
|
|
|
|
|
|
#pod |
197
|
|
|
|
|
|
|
#pod ttie my $scalar, TYPE, $init_val; |
198
|
|
|
|
|
|
|
#pod ttie my @array, TYPE, @init_val; |
199
|
|
|
|
|
|
|
#pod ttie my %hash, TYPE, %init_val; |
200
|
|
|
|
|
|
|
#pod |
201
|
|
|
|
|
|
|
#pod Tie C<$scalar>, C<@array>, or C<%hash> to C and initialize with |
202
|
|
|
|
|
|
|
#pod C<$init_val>, C<@init_val>, or C<%init_val>. |
203
|
|
|
|
|
|
|
#pod |
204
|
|
|
|
|
|
|
#pod =cut |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub ttie (\[$@%]@) { |
207
|
19
|
|
|
19
|
1
|
89019
|
my ($ref, $type, @args) = @_; |
208
|
|
|
|
|
|
|
|
209
|
19
|
|
|
|
|
51
|
my $ref_type; |
210
|
19
|
|
33
|
|
|
157
|
$ref_type = __PACKAGE__->_check_ref_type($ref) // |
211
|
|
|
|
|
|
|
croak "Cannot tie variable of type $ref_type"; |
212
|
|
|
|
|
|
|
|
213
|
19
|
|
|
|
|
110
|
my $pkg = __PACKAGE__ . '::' . ucfirst lc $ref_type; |
214
|
19
|
|
|
|
|
3134
|
require $pkg =~ s|::|/|gr . '.pm'; |
215
|
|
|
|
|
|
|
|
216
|
19
|
|
|
|
|
231
|
&CORE::tie($ref, $pkg, $type, @args); |
217
|
12
|
|
|
|
|
53
|
return $ref; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
#pod =method initialize |
221
|
|
|
|
|
|
|
#pod |
222
|
|
|
|
|
|
|
#pod (tied $scalar)->initialize($init_val); |
223
|
|
|
|
|
|
|
#pod (tied @array)->initialize(@init_val); |
224
|
|
|
|
|
|
|
#pod (tied %hash)->initialize(%init_val); |
225
|
|
|
|
|
|
|
#pod |
226
|
|
|
|
|
|
|
#pod Re-initialize C<$scalar>, C<@array>, or C<%hash>. This is necessary |
227
|
|
|
|
|
|
|
#pod because some types don't allow an empty value, and the variable will |
228
|
|
|
|
|
|
|
#pod temporarily be emptied (except for scalars) if initialized the usual |
229
|
|
|
|
|
|
|
#pod way (e.g., C<@array = qw(foo bar baz)>). This is also more efficient |
230
|
|
|
|
|
|
|
#pod than conventional initialization. |
231
|
|
|
|
|
|
|
#pod |
232
|
|
|
|
|
|
|
#pod See L for more info. |
233
|
|
|
|
|
|
|
#pod |
234
|
|
|
|
|
|
|
#pod =method type |
235
|
|
|
|
|
|
|
#pod |
236
|
|
|
|
|
|
|
#pod my $type = (tied VAR)->type; |
237
|
|
|
|
|
|
|
#pod |
238
|
|
|
|
|
|
|
#pod Return the type constraint for C. Note that the type cannot |
239
|
|
|
|
|
|
|
#pod currently be set, only read. |
240
|
|
|
|
|
|
|
#pod |
241
|
|
|
|
|
|
|
#pod =cut |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
#pod =head1 SEE ALSO |
244
|
|
|
|
|
|
|
#pod |
245
|
|
|
|
|
|
|
#pod =for :list |
246
|
|
|
|
|
|
|
#pod * L |
247
|
|
|
|
|
|
|
#pod |
248
|
|
|
|
|
|
|
#pod =cut |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
1; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
__END__ |