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