1Type::Tiny::Manual::UsiUnsgeWritChoMnotor2i(b3u)ted PerlTyDpoec:u:mTeinntya:t:iMoannual::UsingWithMoo2(3)
2
3
4
6 Type::Tiny::Manual::UsingWithMoo2 - advanced use of Type::Tiny with Moo
7
9 What is a Type?
10 So far all the examples have shown you how to work with types, but we
11 haven't looked at what a type actually is.
12
13 use Types::Standard qw( Int );
14 my $type = Int;
15
16 "Int" in the above code is just a function called with zero arguments
17 which returns a blessed Perl object. It is this object that defines
18 what the Int type is and is responsible for checking values meet its
19 definition.
20
21 use Types::Standard qw( HashRef Int );
22 my $type = HashRef[Int];
23
24 The "HashRef" function, if called with no parameters returns the object
25 defining the HashRef type, just like the "Int" function did before.
26 But the difference here is that it's called with a parameter, an
27 arrayref containing the Int type object. It uses this to make the
28 HashRef[Int] type and returns that.
29
30 Like any object, you can call methods on it. The most important methods
31 to know about are:
32
33 # check the value and return a boolean
34 #
35 $type->check($value);
36
37 # return an error message about $value failing the type check
38 # but don't actually check the value
39 #
40 $type->get_message($value);
41
42 # coerce the value
43 #
44 my $coerced = $type->coerce($value);
45
46 We've already seen some other methods earlier in the tutorial.
47
48 # create a new type, same as the old type, but that has coercions
49 #
50 my $new_type = $type->plus_coercions( ... );
51
52 # different syntax for parameterized types
53 #
54 my $href = HashRef;
55 my $int = Int;
56 my $href_of_int = $href->of($int);
57
58 So now you should understand this:
59
60 use Types::Standard qw( ArrayRef Dict Optional );
61 use Types::Common::Numeric qw( PositiveInt );
62 use Types::Common::String qw( NonEmptyStr );
63
64 my $RaceInfo = Dict[
65 year => PositiveInt,
66 race => NonEmptyStr,
67 jockey => Optional[NonEmptyStr],
68 ];
69
70 has latest_event => ( is => 'rw', isa => $RaceInfo );
71 has wins => ( is => 'rw', isa => ArrayRef[$RaceInfo] );
72 has losses => ( is => 'rw', isa => ArrayRef[$RaceInfo] );
73
74 This can help you avoid repetition if you have a complex parameterized
75 type that you need to reuse a few times.
76
77 "where"
78 One of the most useful methods you can call on a type object is
79 "where".
80
81 use Types::Standard qw( Int );
82
83 has lucky_number => (
84 is => 'ro',
85 isa => Int->where(sub { $_ != 13 }),
86 );
87
88 I think you already understand what it does. It creates a new type
89 constraint on the fly, restricting the original type.
90
91 Like with coercions, these restrictions can be expressed as a coderef
92 or as a string of Perl code, operating on the $_ variable. And like
93 with coercions, using a string of code will result in better
94 performance.
95
96 use Types::Standard qw( Int );
97
98 has lucky_number => (
99 is => 'ro',
100 isa => Int->where(q{ $_ != 13 }),
101 );
102
103 Let's coerce a hashref of strings from an even-sized arrayref of
104 strings:
105
106 use Types::Standard qw( HashRef ArrayRef Str );
107
108 has stringhash => (
109 is => 'ro',
110 isa => HashRef->of(Str)->plus_coercions(
111 ArrayRef->of(Str)->where(q{ @$_ % 2 == 0 }), q{
112 my %h = @$_;
113 \%h;
114 },
115 ),
116 coerce => 1, # never forget!
117 );
118
119 If you understand that, you really are in the advanced class.
120 Congratulations!
121
122 Unions
123 Sometimes you want to accept one thing or another thing. This is pretty
124 easy with Type::Tiny.
125
126 use Types::Standard qw( HashRef ArrayRef Str );
127
128 has strings => (
129 is => 'ro',
130 isa => ArrayRef[Str] | HashRef[Str],
131 );
132
133 Type::Tiny overloads the bitwise or operator so stuff like this should
134 "just work".
135
136 That said, now any code that calls "$self->strings" will probably need
137 to check if the value is an arrayref or a hashref before doing anything
138 with it. So it may be simpler overall if you just choose one of the
139 options and coerce the other one into it.
140
141 Intersections
142 Similar to a union is an intersection.
143
144 package MyAPI::Client {
145 use Moo;
146 use Types::Standard qw( HasMethods InstanceOf );
147
148 has ua => (
149 is => 'ro',
150 isa => (InstanceOf["MyUA"]) & (HasMethods["store_cookie"]),
151 );
152 }
153
154 Here we are checking that the UA is an instance of the MyUA class and
155 also offers the "store_cookie" method. Perhaps "store_cookie" isn't
156 provided by the MyUA class itself, but several subclasses of MyUA
157 provide it.
158
159 Intersections are not useful as often as unions are. This is because
160 they often make no sense. "(ArrayRef) & (HashRef)" would be a reference
161 which was simultaneously pointing to an array and a hash, which is
162 impossible.
163
164 Note that when using intersections, it is good practice to put
165 parentheses around each type. This is to disambiguate the meaning of
166 "&" for Perl, because Perl uses it as the bitwise and operator but also
167 as the sigil for subs.
168
169 Complements
170 For any type Foo there is a complementary type ~Foo (pronounced "not
171 Foo").
172
173 package My::Class {
174 use Moo;
175 use Types::Standard qw( ArrayRef CodeRef );
176
177 has things => ( is => 'ro', isa => ArrayRef[~CodeRef] );
178 }
179
180 "things" is now an arrayref of anything except coderefs.
181
182 If you need a number that is not an integer:
183
184 Num & ~Int
185
186 Types::Standard includes two types which are complements of each other:
187 Undef and Defined.
188
189 NegativeInt might seem to be the complement of PositiveOrZeroInt but
190 when you think about it, it is not. There are values that fall into
191 neither category, such as non-integers, non-numeric strings,
192 references, undef, etc.
193
194 "stringifies_to" and "numifies_to"
195 The Object type constraint provides "stringifies_to" and "numifies_to"
196 methods which are probably best explained by examples.
197
198 "Object->numifies_to(Int)" means any object where "0 + $object" is an
199 integer.
200
201 "Object->stringifies_to(StrMatch[$re])" means any object where
202 "$object" matches the regular expression.
203
204 "Object->stringifies_to($re)" also works as a shortcut.
205
206 "Object->numifies_to($coderef)" and "Object->stringifies_to($coderef)"
207 also work, where the coderef checks $_ and returns a boolean.
208
209 Other types which are also logically objects, such as parameterized
210 HasMethods, InstanceOf, and ConsumerOf should also provide
211 "stringifies_to" and "numifies_to" methods.
212
213 "stringifies_to" and "numifies_to" work on unions if all of the type
214 constraints in the union offer the method.
215
216 "stringifies_to" and "numifies_to" work on intersections if at least
217 one of of the type constraints in the intersection offers the method.
218
219 "with_attribute_values"
220 Another one that is probably best explained using an example:
221
222 package Horse {
223 use Types::Standard qw( Enum Object );
224
225 has gender => (
226 is => 'ro',
227 isa => Enum['m', 'f'],
228 );
229 has father => (
230 is => 'ro',
231 isa => Object->with_attribute_values(gender => Enum['m']),
232 );
233 has mother => (
234 is => 'ro',
235 isa => Object->with_attribute_values(gender => Enum['f']),
236 );
237 }
238
239 In this example when you set a horse's father, it will call
240 "$father->gender" and check that it matches Enum['m'].
241
242 This method is in the same family as "stringifies_as" and
243 "numifies_as", so like those, it only applies to Object and similar
244 type constraints, can work on unions/intersections under the same
245 circumstances, and will also accept coderefs and regexps.
246
247 has father => (
248 is => 'ro',
249 isa => Object->with_attribute_values(gender => sub { $_ eq 'm' }),
250 );
251 has mother => (
252 is => 'ro',
253 isa => Object->with_attribute_values(gender => qr/^f/i),
254 );
255
256 All of "stringifies_as", "numifies_as", and "with_attributes_as" are
257 really just wrappers around "where". The following two are roughly
258 equivalent:
259
260 my $type1 = Object->with_attribute_values(foo => Int, bar => Num);
261
262 my $type2 = Object->where(sub {
263 Int->check( $_->foo ) and Num->check( $_->bar )
264 });
265
266 The first will result in better performing code though.
267
268 Tied Variables
269 It is possible to tie variables to a type constraint.
270
271 use Types::Standard qw(Int);
272
273 tie my $n, Int, 4;
274
275 print "$n\n"; # says "4"
276 $n = 5; # ok
277 $n = "foo"; # dies
278
279 You can also tie arrays:
280
281 tie my @numbers, Int;
282 push @numbers, 1 .. 10;
283
284 And hashes:
285
286 tie my %numbers, Int;
287 $numbers{lucky} = 7;
288 $numbers{unlucky} = 13;
289
290 Earlier in the manual, it was mentioned that there is a problem with
291 code like this:
292
293 push @{ $horse->children }, $non_horse;
294
295 This can be solved using tied variables.
296
297 tie @{ $horse->children }, InstanceOf["Horse"];
298
299 Here is a longer example using builders and triggers.
300
301 package Horse {
302 use Moo;
303 use Types::Standard qw( Str Num ArrayRef InstanceOf );
304 use Type::Params qw( signature );
305 use namespace::autoclean;
306
307 my $ThisClass = InstanceOf[ __PACKAGE__ ];
308
309 has name => ( is => 'ro', isa => Str );
310 has gender => ( is => 'ro', isa => Str );
311 has age => ( is => 'rw', isa => Num );
312 has children => (
313 is => 'rw',
314 isa => ArrayRef[$ThisClass],
315 builder => "_build_children",
316 trigger => sub { shift->_trigger_children(@_) },
317 );
318
319 # tie a default arrayref
320 sub _build_children {
321 my $self = shift;
322 tie my @kids, $ThisClass;
323 \@kids;
324 }
325
326 # this method will tie an arrayref provided by the caller
327 sub _trigger_children {
328 my $self = shift;
329 my ($new) = @_;
330 tie @$new, $ThisClass;
331 }
332
333 sub add_child {
334 state $check = signature(
335 method => $ThisClass,
336 positional => [ $ThisClass ],
337 );
338 my ( $self, $kid ) = &$check;
339 push @{ $self->children }, $kid;
340 return $self;
341 }
342 }
343
344 Now it's pretty much impossible for the caller to make a mess by adding
345 a non-horse as a child.
346
347 (Note there's a Types::Self module on CPAN that will define a Self type
348 meaning InstanceOf[ __PACKAGE__ ] for you!)
349
351 Here's your next step:
352
353 • Type::Tiny::Manual::UsingWithMoo3
354
355 There's more than one way to do it! Alternative ways of using
356 Type::Tiny, including type registries, exported functions, and
357 "dwim_type".
358
360 Toby Inkster <tobyink@cpan.org>.
361
363 This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.
364
365 This is free software; you can redistribute it and/or modify it under
366 the same terms as the Perl 5 programming language system itself.
367
369 THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
370 WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
371 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
372
373
374
375perl v5.36.0 2023-04-2T4ype::Tiny::Manual::UsingWithMoo2(3)