1Type::Tiny::Manual::LibUrsaerrieCso(n3t)ributed Perl DocTuympeen:t:aTtiinoyn::Manual::Libraries(3)
2
3
4
6 Type::Tiny::Manual::Libraries - defining your own type libraries
7
9 Defining a Type
10 A type is an object and you can create a new one using the constructor:
11
12 use Type::Tiny;
13
14 my $type = Type::Tiny->new(%args);
15
16 A full list of the available arguments can be found in the Type::Tiny
17 documentation, but the most important ones to begin with are:
18
19 "name"
20 The name of your new type. Type::Tiny uses a convention of
21 UpperCamelCase names for type constraints. The type name may also
22 begin with one or two leading underscores to indicate a type
23 intended for internal use only. Types using non-ASCII characters
24 may cause problems on older versions of Perl (pre-5.8).
25
26 Although this is optional and types may be anonymous, a name is
27 required for a type constraint to added to a type library.
28
29 "constraint"
30 A code reference checking $_ and returning a boolean.
31 Alternatively, a string of Perl code may be provided.
32
33 If you've been paying attention, you can probably guess that the
34 string of Perl code may result in more efficient type checks.
35
36 "parent"
37 An existing type constraint to inherit from. A value will need to
38 pass the parent constraint before its own constraint would be
39 called.
40
41 my $Even = Type::Tiny->new(
42 name => 'EvenNumber',
43 parent => Types::Standard::Int,
44 constraint => sub {
45 # in this sub we don't need to check that $_ is an Int
46 # because the parent will take care of that!
47
48 $_ % 2 == 0
49 },
50 );
51
52 Although the "parent" is optional, it makes sense whenever possible
53 to inherit from an existing type constraint to benefit from any
54 optimizations or XS implementations they may provide.
55
56 Defining a Library
57 A library is a Perl module that exports type constraints as subs.
58 Types::Standard, Types::Common::Numeric, and Types::Common::String are
59 type libraries that are bundled with Type::Tiny.
60
61 To create a type library, create a package that inherits from
62 Type::Library.
63
64 package MyTypes {
65 use Type::Library -base;
66
67 ...; # your type definitions go here
68 }
69
70 You can add types like this:
71
72 package MyTypes {
73 use Type::Library -base;
74
75 my $Even = Type::Tiny->new(
76 name => 'EvenNumber',
77 parent => Types::Standard::Int,
78 constraint => sub {
79 # in this sub we don't need to check that $_ is an Int
80 # because the parent will take care of that!
81
82 $_ % 2 == 0
83 },
84 );
85
86 __PACKAGE__->add_type($Even);
87 }
88
89 There is a shortcut for adding types if they're going to be blessed
90 Type::Tiny objects and not, for example, a subclass of Type::Tiny. You
91 can just pass %args directly to "add_type".
92
93 package MyTypes {
94 use Type::Library -base;
95
96 __PACKAGE__->add_type(
97 name => 'EvenNumber',
98 parent => Types::Standard::Int,
99 constraint => sub {
100 # in this sub we don't need to check that $_ is an Int
101 # because the parent will take care of that!
102
103 $_ % 2 == 0
104 },
105 );
106 }
107
108 The "add_type" method returns the type it just added, so it can be
109 stored in a variable.
110
111 my $Even = __PACKAGE__->add_type(...);
112
113 This can be useful if you wish to use $Even as the parent type to some
114 other type you're going to define later.
115
116 Here's a bigger worked example:
117
118 package Example::Types {
119 use Type::Library -base;
120 use Types::Standard -types;
121 use DateTime;
122
123 # Type::Tiny::Class is a subclass of Type::Tiny for creating
124 # InstanceOf-like types. It's kind of better though because
125 # it does cool stuff like pass through $type->new(%args) to
126 # the class's constructor.
127 #
128 my $dt = __PACKAGE__->add_type(
129 Type::Tiny::Class->new(
130 name => 'Datetime',
131 class => 'DateTime',
132 )
133 );
134
135 my $dth = __PACKAGE__->add_type(
136 name => 'DatetimeHash',
137 parent => Dict[
138 year => Int,
139 month => Optional[ Int ],
140 day => Optional[ Int ],
141 hour => Optional[ Int ],
142 minute => Optional[ Int ],
143 second => Optional[ Int ],
144 nanosecond => Optional[ Int ],
145 time_zone => Optional[ Str ],
146 ],
147 );
148
149 my $eph = __PACKAGE__->add_type(
150 name => 'EpochHash',
151 parent => Dict[ epoch => Int ],
152 );
153
154 # Can't just use "plus_coercions" method because that creates
155 # a new anonymous child type to add the coercions to. We want
156 # to add them to the type which exists in this library.
157 #
158 $dt->coercion->add_type_coercions(
159 Int, q{ DateTime->from_epoch(epoch => $_) },
160 Undef, q{ DateTime->now() },
161 $dth, q{ DateTime->new(%$_) },
162 $eph, q{ DateTime->from_epoch(%$_) },
163 );
164
165 __PACKAGE__->make_immutable;
166 }
167
168 "make_immutable" freezes to coercions of all the types in the package,
169 so no outside code can tamper with the coercions, and allows Type::Tiny
170 to make optimizations to the coercions, knowing they won't later be
171 altered. You should always do this at the end.
172
173 The library will export types Datetime, DatetimeHash, and EpochHash.
174 The Datetime type will have coercions from Int, Undef, DatetimeHash,
175 and EpochHash.
176
177 Extending Libraries
178 Type::Utils provides a helpful function "extends".
179
180 package My::Types {
181 use Type::Library -base;
182 use Type::Utils qw( extends );
183
184 BEGIN { extends("Types::Standard") };
185
186 # define your own types here
187 }
188
189 The "extends" function (which you should usually use in a "BEGIN { }"
190 block not only loads another type library, but it also adds all the
191 types from it to your library.
192
193 This means code using the above My::Types doesn't need to do:
194
195 use Types::Standard qw( Str );
196 use My::Types qw( Something );
197
198 It can just do:
199
200 use My::Types qw( Str Something );
201
202 Because all the types from Types::Standard have been copied across into
203 My::Types and are also available there.
204
205 "extends" can be passed a list of libraries; you can inherit from
206 multiple existing libraries. It can also recognize and import types
207 from MooseX::Types, MouseX::Types, and Specio::Exporter libraries.
208
209 Custom Error Messages
210 A type constraint can have custom error messages. It's pretty simple:
211
212 Type::Tiny->new(
213 name => 'EvenNumber',
214 parent => Types::Standard::Int,
215 constraint => sub {
216 # in this sub we don't need to check that $_ is an Int
217 # because the parent will take care of that!
218
219 $_ % 2 == 0
220 },
221 message => sub {
222 sprintf '%s is not an even number', Type::Tiny::_dd($_);
223 },
224 );
225
226 The message coderef just takes a value in $_ and returns a string. It
227 may use "Type::Tiny::_dd()" as a way of pretty-printing a value.
228 (Don't be put off by the underscore in the function name. "_dd()" is an
229 officially supported part of Type::Tiny's API now.)
230
231 You don't have to use "_dd()". You can generate any error string you
232 like. But "_dd()" will help you make undef and the empty string look
233 different, and will pretty-print references, and so on.
234
235 There's no need to supply an error message coderef unless you really
236 want custom error messages. The default sub should be reasonable.
237
238 Inlining
239 In Perl, sub calls are relatively expensive in terms of memory and CPU
240 use. The PositiveInt type inherits from Int which inherits from Num
241 which inherits from Str which inherits from Defined which inherits from
242 Item which inherits from Any.
243
244 So you might think that to check of $value is a PositiveInt, it needs
245 to be checked all the way up the inheritance chain. But this is where
246 one of Type::Tiny's big optimizations happens. Type::Tiny can glue
247 together a bunch of checks with a stringy eval, and get a single
248 coderef that can do all the checks in one go.
249
250 This is why when Type::Tiny gives you a choice of using a coderef or a
251 string of Perl code, you should usually choose the string of Perl code.
252 A single coderef can "break the chain".
253
254 But these automatically generated strings of Perl code are not always
255 as efficient as they could be. For example, imagine that HashRef is
256 defined as:
257
258 my $Defined = Type::Tiny->new(
259 name => 'Defined',
260 constraint => 'defined($_)',
261 );
262 my $Ref = Type::Tiny->new(
263 name => 'Ref',
264 parent => $Defined,
265 constraint => 'ref($_)',
266 );
267 my $HashRef = Type::Tiny->new(
268 name => 'HashRef',
269 parent => $Ref,
270 constraint => 'ref($_) eq "HASH"',
271 );
272
273 Then the combined check is:
274
275 defined($_) and ref($_) and ref($_) eq "HASH"
276
277 Actually in practice it's even more complicated, because Type::Tiny
278 needs to localize and set $_ first.
279
280 But in practice, the following should be a sufficient check:
281
282 ref($_) eq "HASH"
283
284 It is possible for the HashRef type to have more control over the
285 string of code generated.
286
287 my $HashRef = Type::Tiny->new(
288 name => 'HashRef',
289 parent => $Ref,
290 constraint => 'ref($_) eq "HASH"',
291 inlined => sub {
292 my $varname = pop;
293 sprintf 'ref(%s) eq "HASH"', $varname;
294 },
295 );
296
297 The inlined coderef gets passed the name of a variable to check. This
298 could be '$_' or '$var' or "$some{deep}{thing}[0]". Because it is
299 passed the name of a variable to check, instead of always checking $_,
300 this enables very efficient checking for parameterized types.
301
302 Although in this case, the inlining coderef is just returning a string,
303 technically it returns a list of strings. If there's multiple strings,
304 Type::Tiny will join them together in a big "&&" statement.
305
306 As a special case, if the first item in the returned list of strings is
307 undef, then Type::Tiny will substitute the parent type constraint's
308 inlined string in its place. So an inlieing coderef for even numbers
309 might be:
310
311 Type::Tiny->new(
312 name => 'EvenNumber',
313 parent => Types::Standard::Int,
314 constraint => sub { $_ % 2 == 0 },
315 inlined => sub {
316 my $varname = pop;
317 return (undef, "$varname % 2 == 0");
318 },
319 );
320
321 Even if you provide a coderef as a string, an inlining coderef has the
322 potential to generate more efficient code, so you should consider
323 providing one.
324
325 Pre-Declaring Types
326 use Type::Library -base,
327 -declare => qw( Foo Bar Baz );
328
329 This declares types Foo, Bar, and Baz at compile time so they can
330 safely be used as barewords in your type library.
331
332 This also allows recursively defined types to (mostly) work!
333
334 use Type::Library -base,
335 -declare => qw( NumericArrayRef );
336 use Types::Standard qw( Num ArrayRef );
337
338 __PACKAGE__->add_type(
339 name => NumericArrayRef,
340 parent => ArrayRef->of( Num | NumericArrayRef ),
341 );
342
343 (Support for recursive type definitions added in Type::Library
344 1.009_000.)
345
346 Parameterizable Types
347 This is probably the most "meta" concept that is going to be covered.
348 Building your own type constraint that can be parameterized like
349 ArrayRef or HasMethods.
350
351 The type constraint we'll build will be MultipleOf[$i] which checks
352 that an integer is a multiple of $i.
353
354 __PACKAGE__->add_type(
355 name => 'MultipleOf',
356 parent => Int,
357
358 # This coderef gets passed the contents of the square brackets.
359 constraint_generator => sub {
360 my $i = assert_Int(shift);
361 # needs to return a coderef to use as a constraint for the
362 # parameterized type
363 return sub { $_ % $i == 0 };
364 },
365
366 # optional but recommended
367 inline_generator => sub {
368 my $i = shift;
369 return sub {
370 my $varname = pop;
371 return (undef, "$varname % $i == 0");
372 };
373 },
374
375 # probably the most complex bit
376 coercion_generator => sub {
377 my $i = $_[2];
378 require Type::Coercion;
379 return Type::Coercion->new(
380 type_coercion_map => [
381 Num, qq{ int($i * int(\$_/$i)) }
382 ],
383 );
384 },
385 );
386
387 Now we can define an even number like this:
388
389 __PACKAGE__->add_type(
390 name => 'EvenNumber',
391 parent => __PACKAGE__->get_type('MultipleOf')->of(2),
392 coercion => 1, # inherit from parent
393 );
394
395 Note that it is possible for a type constraint to have a "constraint"
396 and a "constraint_generator".
397
398 BaseType # uses the constraint
399 BaseType[] # constraint_generator with no arguments
400 BaseType[$x] # constraint_generator with an argument
401
402 In the MultipleOf example above, MultipleOf[] with no number would
403 throw an error because of "assert_Int(shift)" not finding an integer.
404
405 But it is certainly possible for BaseType[] to be meaningful and
406 distinct from "BaseType".
407
408 For example, Tuple is just the same as ArrayRef and accepts any
409 arrayref as being valid. But Tuple[] will only accept arrayrefs with
410 zero elements in them. (Just like Tuple[Any,Any] will only accept
411 arrayrefs with two elements.)
412
414 After that last example, probably have a little lie down. Once you're
415 recovered, here's your next step:
416
417 ยท Type::Tiny::Manual::UsingWithMoose
418
419 How to use Type::Tiny with Moose, including the advantages of
420 Type::Tiny over built-in type constraints, and Moose-specific
421 features.
422
424 Toby Inkster <tobyink@cpan.org>.
425
427 This software is copyright (c) 2013-2014, 2017-2020 by Toby Inkster.
428
429 This is free software; you can redistribute it and/or modify it under
430 the same terms as the Perl 5 programming language system itself.
431
433 THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
434 WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
435 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
436
437
438
439perl v5.32.0 2020-09-17 Type::Tiny::Manual::Libraries(3)