line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2023 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Signature::Attribute::Checked 0.01; |
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
798083
|
use v5.14; |
|
4
|
|
|
|
|
33
|
|
9
|
4
|
|
|
4
|
|
26
|
use warnings; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
506
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
require XSLoader; |
12
|
|
|
|
|
|
|
XSLoader::load( __PACKAGE__, our $VERSION ); |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
C - apply value constraint checks to subroutine parameters |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 SYNOPSIS |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
With L |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
use v5.26; |
23
|
|
|
|
|
|
|
use Sublike::Extended; |
24
|
|
|
|
|
|
|
use Signature::Attribute::Checked; |
25
|
|
|
|
|
|
|
use Types::Standard qw( Num ); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
extended sub add ($x :Checked(Num), $y :Checked(Num)) { |
28
|
|
|
|
|
|
|
return $x + $y; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
say add(10, 20); # this is fine |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
say add("hello", "world"); # throws an exception |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
This module provides a third-party subroutine parameter attribute via |
38
|
|
|
|
|
|
|
L, which declares that values passed to a subroutine must |
39
|
|
|
|
|
|
|
conform to a given constraint check. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
B The ability for sublike constructions to take third-party parameter |
42
|
|
|
|
|
|
|
attributes is still new and highly experimental, and subject to much API |
43
|
|
|
|
|
|
|
change in future. As a result, this module should be considered equally |
44
|
|
|
|
|
|
|
experimental. Core perl's parser does not permit parameters to take |
45
|
|
|
|
|
|
|
attributes. This ability must be requested specially; either by using |
46
|
|
|
|
|
|
|
L, or perhaps enabled directly by some other sublike |
47
|
|
|
|
|
|
|
keyword using the C infrastructure. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
Additionally, the behaviour provided by this module should be considered more |
50
|
|
|
|
|
|
|
of a work-in-progress stepping stone. Ideally, constraint syntax ought |
51
|
|
|
|
|
|
|
to be provided in a much more fundamental way by Perl itself, allowing it to |
52
|
|
|
|
|
|
|
be used on C lexicals, class fields, and other places as well as |
53
|
|
|
|
|
|
|
subroutine parameters. This module is more of a placeholder to allow some part |
54
|
|
|
|
|
|
|
of that behaviour to be specified for subroutine parameters, while not getting |
55
|
|
|
|
|
|
|
in the way of a more general, more powerful system being added in future. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head1 PARAMETER ATTRIBUTES |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head2 :Checked |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
extended sub f($x :Checked(EXPRESSION)) { ... } |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
Declares that any value passed to the parameter at the time the subroutine is |
64
|
|
|
|
|
|
|
called must conform to the constraint checker specified by the expression. |
65
|
|
|
|
|
|
|
Attempts to pass a non-conforming value will throw an exception and the |
66
|
|
|
|
|
|
|
subroutine body will not be invoked. Currently only scalar parameters are |
67
|
|
|
|
|
|
|
supported. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
At compiletime, the string given by I is C'ed in scalar |
70
|
|
|
|
|
|
|
context, and its result is stored as part of the subroutine's definition. The |
71
|
|
|
|
|
|
|
expression must yield either an object reference, a code reference, or a |
72
|
|
|
|
|
|
|
string containing the name of a package. In the case of an object or package, |
73
|
|
|
|
|
|
|
a method called C must exist on it. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
If using a plain package name as a checker, be sure to quote package names so |
76
|
|
|
|
|
|
|
it will not upset C |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
extended sub xyz ($x :Checked('CheckerPackage')) { ... } |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
At runtime, this constraint checker is used every time an attempt is made to |
81
|
|
|
|
|
|
|
call the function. The checker is used as the invocant for invoking a C |
82
|
|
|
|
|
|
|
method, and the value for the parameter is passed as an argument. If the |
83
|
|
|
|
|
|
|
method returns true, the call is allowed. If false, it is rejected with an |
84
|
|
|
|
|
|
|
exception and the function body is not invoked. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
$ok = $checkerobj->check( $value ); # if an object |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
$ok = $checkersub->( $value ); # if a code reference |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
$ok = $checkerpkg->check( $value ); # if a package name |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
(For performance reasons, the C method is actually resolved into a |
93
|
|
|
|
|
|
|
function at compiletime when the C<:Checked> attribute is applied, and this |
94
|
|
|
|
|
|
|
stored function is the one that is called at assignment time. If the method |
95
|
|
|
|
|
|
|
itself is replaced later by globref assignment or other trickery, this updated |
96
|
|
|
|
|
|
|
function will not be used.) |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
As this is the interface supported by L, any constraint |
99
|
|
|
|
|
|
|
object provided by that module is already supported here. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
use Types::Standard qw( Str Num ); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
extended sub ghi ($x :Checked(Str), $y :Checked(Num)) { ... } |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
It is not specified what order these checks are performed in. In particular, |
106
|
|
|
|
|
|
|
if any parameter default expressions invoke any side-effects, it is |
107
|
|
|
|
|
|
|
unspecified whether such side-effects will happen if a value passed for a |
108
|
|
|
|
|
|
|
parameter fails its constraint check. Users should take care not to attempt to |
109
|
|
|
|
|
|
|
invoke any side-effects during such expressions. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Note further that these value checks are only performed once, at the time the |
112
|
|
|
|
|
|
|
subroutine is invoked. Code within the body of the subroutine can freely |
113
|
|
|
|
|
|
|
assign any other kind of value to the variable corresponding to a C<:Checked> |
114
|
|
|
|
|
|
|
parameter without issue. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
extended sub foo ($n :Checked(Num)) { |
117
|
|
|
|
|
|
|
$x = "seven"; # this is permitted |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=cut |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub import |
123
|
|
|
|
|
|
|
{ |
124
|
3
|
|
|
3
|
|
117
|
$^H{"Signature::Attribute::Checked/Checked"}++; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub unimport |
128
|
|
|
|
|
|
|
{ |
129
|
0
|
|
|
0
|
|
|
delete $^H{"Signature::Attribute::Checked/Checked"}; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=head1 SEE ALSO |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=over 4 |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item * |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
L - apply value constraint checks to C fields |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=back |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=head1 AUTHOR |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
Paul Evans |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=cut |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
0x55AA; |