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 This feature requires Type::Tie which is a separate thing to install.
280 Type::Tiny will automatically load Type::Tie in the background if it
281 detects you're trying to tie a variable to a type.
282
283 You can also tie arrays:
284
285 tie my @numbers, Int;
286 push @numbers, 1 .. 10;
287
288 And hashes:
289
290 tie my %numbers, Int;
291 $numbers{lucky} = 7;
292 $numbers{unlucky} = 13;
293
294 Earlier in the manual, it was mentioned that there is a problem with
295 code like this:
296
297 push @{ $horse->children }, $non_horse;
298
299 This can be solved using tied variables.
300
301 tie @{ $horse->children }, InstanceOf["Horse"];
302
303 Here is a longer example using builders and triggers.
304
305 package Horse {
306 use Moo;
307 use Types::Standard qw( Str Num ArrayRef InstanceOf );
308 use Type::Params qw(compile);
309 use namespace::autoclean;
310
311 my $ThisClass = InstanceOf[ __PACKAGE__ ];
312
313 has name => ( is => 'ro', isa => Str );
314 has gender => ( is => 'ro', isa => Str );
315 has age => ( is => 'rw', isa => Num );
316 has children => (
317 is => 'rw',
318 isa => ArrayRef[$ThisClass],
319 builder => "_build_children",
320 trigger => sub { shift->_trigger_children(@_) },
321 );
322
323 # tie a default arrayref
324 sub _build_children {
325 my $self = shift;
326 tie my @kids, $ThisClass;
327 \@kids;
328 }
329
330 # this method will tie an arrayref provided by the caller
331 sub _trigger_children {
332 my $self = shift;
333 my ($new) = @_;
334 tie @$new, $ThisClass;
335 }
336
337 sub add_child {
338 state $check = compile($ThisClass, $ThisClass);
339 my ($self, $kid) = &$check;
340 push @{ $self->children }, $kid;
341 return $self;
342 }
343 }
344
345 Now it's pretty much impossible for the caller to make a mess by adding
346 a non-horse as a child.
347
349 Here's your next step:
350
351 ยท Type::Tiny::Manual::UsingWithMoo3
352
353 There's more than one way to do it! Alternative ways of using
354 Type::Tiny, including type registries, exported functions, and
355 "dwim_type".
356
358 Toby Inkster <tobyink@cpan.org>.
359
361 This software is copyright (c) 2013-2014, 2017-2020 by Toby Inkster.
362
363 This is free software; you can redistribute it and/or modify it under
364 the same terms as the Perl 5 programming language system itself.
365
367 THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
368 WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
369 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
370
371
372
373perl v5.32.0 2020-09-1T7ype::Tiny::Manual::UsingWithMoo2(3)