README.md
1# NAME
2
3Specio - Type constraints and coercions for Perl
4
5# VERSION
6
7version 0.47
8
9# SYNOPSIS
10
11 package MyApp::Type::Library;
12
13 use Specio::Declare;
14 use Specio::Library::Builtins;
15
16 declare(
17 'PositiveInt',
18 parent => t('Int'),
19 inline => sub {
20 $_[0]->parent->inline_check( $_[1] )
21 . ' && ( '
22 . $_[1]
23 . ' > 0 )';
24 },
25 );
26
27 # or ...
28
29 declare(
30 'PositiveInt',
31 parent => t('Int'),
32 where => sub { $_[0] > 0 },
33 );
34
35 declare(
36 'ArrayRefOfPositiveInt',
37 parent => t(
38 'ArrayRef',
39 of => t('PositiveInt'),
40 ),
41 );
42
43 coerce(
44 'ArrayRefOfPositiveInt',
45 from => t('PositiveInt'),
46 using => sub { [ $_[0] ] },
47 );
48
49 any_can_type(
50 'Duck',
51 methods => [ 'duck_walk', 'quack' ],
52 );
53
54 object_isa_type('MyApp::Person');
55
56# DESCRIPTION
57
58The `Specio` distribution provides classes for representing type constraints
59and coercion, along with syntax sugar for declaring them.
60
61Note that this is not a proper type system for Perl. Nothing in this
62distribution will magically make the Perl interpreter start checking a value's
63type on assignment to a variable. In fact, there's no built-in way to apply a
64type to a variable at all.
65
66Instead, you can explicitly check a value against a type, and optionally coerce
67values to that type.
68
69My long-term goal is to replace Moose's built-in types and [MooseX::Types](https://metacpan.org/pod/MooseX%3A%3ATypes)
70with this module.
71
72# WHAT IS A TYPE?
73
74At it's core, a type is simply a constraint. A constraint is code that checks a
75value and returns true or false. Most constraints are represented by
76[Specio::Constraint::Simple](https://metacpan.org/pod/Specio%3A%3AConstraint%3A%3ASimple) objects. However, there are other type constraint
77classes for specialized kinds of constraints.
78
79Types can be named or anonymous, and each type can have a parent type. A type's
80constraint is optional because sometimes you may want to create a named subtype
81of some existing type without adding additional constraints.
82
83Constraints can be expressed either in terms of a simple subroutine reference
84or in terms of an inline generator subroutine reference. The former is easier
85to write but the latter is preferred because it allow for better optimization.
86
87A type can also have an optional message generator subroutine reference. You
88can use this to provide a more intelligent error message when a value does not
89pass the constraint, though the default message should suffice for most cases.
90
91Finally, you can associate a set of coercions with a type. A coercion is a
92subroutine reference (or inline generator, like constraints), that takes a
93value of one type and turns it into a value that matches the type the coercion
94belongs to.
95
96# BUILTIN TYPES
97
98This distribution ships with a set of builtin types representing the types
99provided by the Perl interpreter itself. They are arranged in a hierarchy as
100follows:
101
102 Item
103 Bool
104 Maybe (of `a)
105 Undef
106 Defined
107 Value
108 Str
109 Num
110 Int
111 ClassName
112 Ref
113 ScalarRef (of `a)
114 ArrayRef (of `a)
115 HashRef (of `a)
116 CodeRef
117 RegexpRef
118 GlobRef
119 FileHandle
120 Object
121
122The `Item` type accepts anything and everything.
123
124The `Bool` type only accepts `undef`, `0`, or `1`.
125
126The `Undef` type only accepts `undef`.
127
128The `Defined` type accepts anything _except_ `undef`.
129
130The `Num` and `Int` types are stricter about numbers than Perl is.
131Specifically, they do not allow any sort of space in the number, nor do they
132accept "Nan", "Inf", or "Infinity".
133
134The `ClassName` type constraint checks that the name is valid _and_ that the
135class is loaded.
136
137The `FileHandle` type accepts either a glob, a scalar filehandle, or anything
138that isa [IO::Handle](https://metacpan.org/pod/IO%3A%3AHandle).
139
140All types accept overloaded objects that support the required operation. See
141below for details.
142
143## Overloading
144
145Perl's overloading is horribly broken and doesn't make much sense at all.
146
147However, unlike Moose, all type constraints allow overloaded objects where they
148make sense.
149
150For types where overloading makes sense, we explicitly check that the object
151provides the type overloading we expect. We _do not_ simply try to use the
152object as the type in question and hope it works. This means that these checks
153effectively ignore the `fallback` setting for the overloaded object. In other
154words, an object that overloads stringification will not pass the `Bool` type
155check unless it _also_ overloads boolification.
156
157Most types do not check that the overloaded method actually returns something
158that matches the constraint. This may change in the future.
159
160The `Bool` type accepts an object that implements `bool` overloading.
161
162The `Str` type accepts an object that implements string (`q{""}`)
163overloading.
164
165The `Num` type accepts an object that implements numeric (`'0+'}`)
166overloading. The `Int` type does as well, but it will check that the
167overloading returns an actual integer.
168
169The `ClassName` type will accept an object with string overloading that
170returns a class name.
171
172To make this all more confusing, the `Value` type will _never_ accept an
173object, even though some of its subtypes will.
174
175The various reference types all accept objects which provide the appropriate
176overloading. The `FileHandle` type accepts an object which overloads
177globification as long as the returned glob is an open filehandle.
178
179# PARAMETERIZABLE TYPES
180
181Any type followed by a type parameter `` of `a `` in the hierarchy above can be
182parameterized. The parameter is itself a type, so you can say you want an
183"ArrayRef of Int", or even an "ArrayRef of HashRef of ScalarRef of ClassName".
184
185When they are parameterized, the `ScalarRef` and `ArrayRef` types check that
186the value(s) they refer to match the type parameter. For the `HashRef` type,
187the parameter applies to the values (keys are never checked).
188
189## Maybe
190
191The `Maybe` type is a special parameterized type. It allows for either
192`undef` or a value. All by itself, it is meaningless, since it is equivalent
193to "Maybe of Item", which is equivalent to Item. When parameterized, it accepts
194either an `undef` or the type of its parameter.
195
196This is useful for optional attributes or parameters. However, you're probably
197better off making your code simply not pass the parameter at all This usually
198makes for a simpler API.
199
200# REGISTRIES AND IMPORTING
201
202Types are local to each package where they are used. When you "import" types
203from some other library, you are actually making a copy of that type.
204
205This means that a type named "Foo" in one package may not be the same as "Foo"
206in another package. This has potential for confusion, but it also avoids the
207magic action at a distance pollution that comes with a global type naming
208system.
209
210The registry is managed internally by the Specio distribution's modules, and is
211not exposed to your code. To access a type, you always call `t('TypeName')`.
212
213This returns the named type or dies if no such type exists.
214
215Because types are always copied on import, it's safe to create coercions on any
216type. Your coercion from `Str` to `Int` will not be seen by any other
217package, unless that package explicitly imports your `Int` type.
218
219When you import types, you import every type defined in the package you import
220from. However, you _can_ overwrite an imported type with your own type
221definition. You _cannot_ define the same type twice internally.
222
223# CREATING A TYPE LIBRARY
224
225By default, all types created inside a package are invisible to other packages.
226If you want to create a type library, you need to inherit from
227[Specio::Exporter](https://metacpan.org/pod/Specio%3A%3AExporter) package:
228
229 package MyApp::Type::Library;
230
231 use parent 'Specio::Exporter';
232
233 use Specio::Declare;
234 use Specio::Library::Builtins;
235
236 declare(
237 'Foo',
238 parent => t('Str'),
239 where => sub { $_[0] =~ /foo/i },
240 );
241
242Now the MyApp::Type::Library package will export a single type named `Foo`. It
243_does not_ re-export the types provided by [Specio::Library::Builtins](https://metacpan.org/pod/Specio%3A%3ALibrary%3A%3ABuiltins).
244
245If you want to make your library re-export some other libraries types, you can
246ask for this explicitly:
247
248 package MyApp::Type::Library;
249
250 use parent 'Specio::Exporter';
251
252 use Specio::Declare;
253 use Specio::Library::Builtins -reexport;
254
255 declare( 'Foo, ... );
256
257Now MyApp::Types::Library exports any types it defines, as well as all the
258types defined in [Specio::Library::Builtins](https://metacpan.org/pod/Specio%3A%3ALibrary%3A%3ABuiltins).
259
260# DECLARING TYPES
261
262Use the [Specio::Declare](https://metacpan.org/pod/Specio%3A%3ADeclare) module to declare types. It exports a set of helpers
263for declaring types. See that module's documentation for more details on these
264helpers.
265
266# USING SPECIO WITH [Moose](https://metacpan.org/pod/Moose)
267
268This should just work. Use a Specio type anywhere you'd specify a type.
269
270# USING SPECIO WITH [Moo](https://metacpan.org/pod/Moo)
271
272Using Specio with Moo is easy. You can pass Specio constraint objects as `isa`
273parameters for attributes. For coercions, simply call `$type->coercion_sub`.
274
275 package Foo;
276
277 use Specio::Declare;
278 use Specio::Library::Builtins;
279 use Moo;
280
281 my $str_type = t('Str');
282 has string => (
283 is => 'ro',
284 isa => $str_type,
285 );
286
287 my $ucstr = declare(
288 'UCStr',
289 parent => t('Str'),
290 where => sub { $_[0] =~ /^[A-Z]+$/ },
291 );
292
293 coerce(
294 $ucstr,
295 from => t('Str'),
296 using => sub { return uc $_[0] },
297 );
298
299 has ucstr => (
300 is => 'ro',
301 isa => $ucstr,
302 coerce => $ucstr->coercion_sub,
303 );
304
305The subs returned by Specio use [Sub::Quote](https://metacpan.org/pod/Sub%3A%3AQuote) internally and are suitable for
306inlining.
307
308# USING SPECIO WITH OTHER THINGS
309
310See [Specio::Constraint::Simple](https://metacpan.org/pod/Specio%3A%3AConstraint%3A%3ASimple) for the API that all constraint objects
311share.
312
313# [Moose](https://metacpan.org/pod/Moose), [MooseX::Types](https://metacpan.org/pod/MooseX%3A%3ATypes), and Specio
314
315This module aims to supplant both [Moose](https://metacpan.org/pod/Moose)'s built-in type system (see
316[Moose::Util::TypeConstraints](https://metacpan.org/pod/Moose%3A%3AUtil%3A%3ATypeConstraints) aka MUTC) and [MooseX::Types](https://metacpan.org/pod/MooseX%3A%3ATypes), which attempts
317to patch some of the holes in the Moose built-in type design.
318
319Here are some of the salient differences:
320
321- Types names are strings, but they're not global
322
323 Unlike Moose and MooseX::Types, type names are always local to the current
324 package. There is no possibility of name collision between different modules,
325 so you can safely use short type names.
326
327 Unlike MooseX::Types, types are strings, so there is no possibility of
328 colliding with existing class or subroutine names.
329
330- No type auto-creation
331
332 Types are always retrieved using the `t()` subroutine. If you pass an unknown
333 name to this subroutine it dies. This is different from Moose and
334 MooseX::Types, which assume that unknown names are class names.
335
336- Anon types are explicit
337
338 With [Moose](https://metacpan.org/pod/Moose) and [MooseX::Types](https://metacpan.org/pod/MooseX%3A%3ATypes), you use the same subroutine, `subtype()`,
339 to declare both named and anonymous types. With Specio, you use `declare()`
340 for named types and `anon()` for anonymous types.
341
342- Class and object types are separate
343
344 Moose and MooseX::Types have `class_type` and `duck_type`. The former type
345 requires an object, while the latter accepts a class name or object.
346
347 With Specio, the distinction between accepting an object versus object or class
348 is explicit. There are six declaration helpers, `object_can_type`,
349 `object_does_type`, `object_isa_type`, `any_can_type`, `any_does_type`, and
350 `any_isa_type`.
351
352- Overloading support is baked in
353
354 Perl's overloading is quite broken but ignoring it makes Moose's type system
355 frustrating to use in many cases.
356
357- Types can either have a constraint or inline generator, not both
358
359 Moose and MooseX::Types types can be defined with a subroutine reference as the
360 constraint, an inline generator subroutine, or both. This is purely for
361 backwards compatibility, and it makes the internals more complicated than they
362 need to be.
363
364 With Specio, a constraint can have _either_ a subroutine reference or an
365 inline generator, not both.
366
367- Coercions can be inlined
368
369 I simply never got around to implementing this in Moose.
370
371- No crazy coercion features
372
373 Moose has some bizarre (and mostly) undocumented features relating to coercions
374 and parameterizable types. This is a misfeature.
375
376# OPTIONAL PREREQS
377
378There are several optional prereqs that if installed will make this
379distribution better in some way.
380
381- [Ref::Util](https://metacpan.org/pod/Ref%3A%3AUtil)
382
383 Installing this will speed up a number of type checks for built-in types.
384
385- [XString](https://metacpan.org/pod/XString)
386
387 If this is installed it will be loaded instead of the [B](https://metacpan.org/pod/B) module if you have
388 Perl 5.10 or greater. This module is much more memory efficient than loading
389 all of [B](https://metacpan.org/pod/B).
390
391- [Sub::Util](https://metacpan.org/pod/Sub%3A%3AUtil) or [Sub::Name](https://metacpan.org/pod/Sub%3A%3AName)
392
393 If one of these is installed then stack traces that end up in Specio code will
394 have much better subroutine names for any frames.
395
396# WHY THE NAME?
397
398This distro was originally called "Type", but that's an awfully generic top
399level namespace. Specio is Latin for for "look at" and "spec" is the root for
400the word "species". It's short, relatively easy to type, and not used by any
401other distro.
402
403# LONG-TERM PLANS
404
405Eventually I'd like to see this distro replace Moose's internal type system,
406which would also make MooseX::Types obsolete.
407
408# SUPPORT
409
410Bugs may be submitted at [https://github.com/houseabsolute/Specio/issues](https://github.com/houseabsolute/Specio/issues).
411
412I am also usually active on IRC as 'autarch' on `irc://irc.perl.org`.
413
414# SOURCE
415
416The source code repository for Specio can be found at [https://github.com/houseabsolute/Specio](https://github.com/houseabsolute/Specio).
417
418# DONATIONS
419
420If you'd like to thank me for the work I've done on this module, please
421consider making a "donation" to me via PayPal. I spend a lot of free time
422creating free software, and would appreciate any support you'd care to offer.
423
424Please note that **I am not suggesting that you must do this** in order for me
425to continue working on this particular software. I will continue to do so,
426inasmuch as I have in the past, for as long as it interests me.
427
428Similarly, a donation made in this way will probably not make me work on this
429software much more, unless I get so many donations that I can consider working
430on free software full time (let's all have a chuckle at that together).
431
432To donate, log into PayPal and send money to autarch@urth.org, or use the
433button at [https://www.urth.org/fs-donation.html](https://www.urth.org/fs-donation.html).
434
435# AUTHOR
436
437Dave Rolsky <autarch@urth.org>
438
439# CONTRIBUTORS
440
441- Chris White <chrisw@leehayes.com>
442- cpansprout <cpansprout@gmail.com>
443- Graham Knop <haarg@haarg.org>
444- Karen Etheridge <ether@cpan.org>
445
446# COPYRIGHT AND LICENSE
447
448This software is Copyright (c) 2012 - 2021 by Dave Rolsky.
449
450This is free software, licensed under:
451
452 The Artistic License 2.0 (GPL Compatible)
453
454The full text of the license can be found in the
455`LICENSE` file included with this distribution.
456