From: Dave Rolsky Date: Thu, 29 Jan 2009 18:05:14 +0000 (+0000) Subject: Types manual page X-Git-Tag: 0.66~27^2~11 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=ecd7cc3e0612c7d077e50fc5923e40ad95b32dc1;p=gitmo%2FMoose.git Types manual page --- diff --git a/lib/Moose/Manual/Types.pod b/lib/Moose/Manual/Types.pod new file mode 100644 index 0000000..aa9c73b --- /dev/null +++ b/lib/Moose/Manual/Types.pod @@ -0,0 +1,412 @@ +=pod + +=head1 NAME + +Moose::Manual::Types - Moose's Type System + +=head1 TYPES IN PERL? + +Moose provides its own type system for your class's attributes. You +can also use these types to validate method parameters with the help +of some MooseX modules. + +Moose's type system is based on a combination of Perl 5's own +I types, and some Perl 6 concepts as well. But most +importantly, you can easily create your own subtypes with custom +constraints, making it easy to express any sort of validation. + +You can also name types and re-use them by name, making it easy to +share types throughout a large application. + +Let us be clear that is not a "real" type system. Moose does not +magically make Perl start associating types with variables. In many +ways, this is really an advanced parameter checking system which +allows you to associate a name with a constraintq. + +That said, it's still pretty damn useful, and we think it's one of the +things that makes Moose both fun and powerful. Taking advantage of the +type system makes it much easier to ensure that you are getting valid +data, and it also contributes greatly to code maintainability. + +=head1 THE TYPES + +The basic Moose type hierarchy looks like this + + Any + Item + Bool + Maybe[`a] + Undef + Defined + Value + Num + Int + Str + ClassName + Ref + ScalarRef + ArrayRef[`a] + HashRef[`a] + CodeRef + RegexpRef + GlobRef + FileHandle + Object + Role + +In practice, the only difference between C and C is +conceptual. C is used as the top-level type in the hierarchy. + +The rest of these types correspond to existing Perl concepts. For +example, a C is anything that Perl thinks looks like a number. An +C is a blessed reference, etc. + +The types followed by "[`a]" can be parameterized. So instead of just +plain C we can say that we want C instead. We +can even do something like C. + +The C type deserves a special mention. Used by itself, it +doesn't really mean anything (and is equivalent to C). When it +is parameterized, it means that the value is either C or the +parameterized type. So C means an integer or C + +For more details on the type hierarchy, see +L. + +=head1 WHAT IS A TYPE? + +It's important to realize that types are not classes (or +packages). Types are just objects (L +objects, to be exact) with a name. Moose maintains a global type +registry that lets it convert names like "Num" into the appropriuate +object. + +However, class names I type names. When you define a new class +using Moose, it defines an associated type name behind the scenes: + + package MyApp::User; + + use Moose; + +Now you can use C<'MyApp::User'> as a type name: + + has creator => ( + is => 'rw', + isa => 'MyApp::User', + ); + +However, for non-Moose classes there's no magic. You may have to +explicitly declare the class type. This is a bit muddled because Moose +assumes that any unknown type name passed as the C value for an +attribute is a class. So this works: + + has 'birth_date' => ( + is => 'rw', + isa => 'DateTime', + ); + +In general, when Moose is presented with an unknown name, it assumes +that the name is a class: + + subtype 'ModernDateTime' + => as 'DateTime' + => where { $_->year() >= 1980 } + => message { 'The date you provided is not modern enough' }; + + has 'valid_dates' => ( + is => 'ro', + isa => 'ArrayRef[DateTime]', + ); + +Moose will assume that "DateTime" is a class name and create a type +accordingly. + +=head1 SUBTYPES + +Moose uses subtypes in its built-in hierarchy. C is a child of +C for example. + +A subtype is defined in terms of a parent type and a constraint. Any +constraints defined by the parent(s) will be checked first, and then +the subtype's constraint is checked. A value must pass I of these +checks to be valid for the subtype. + +Generally, a subtype takes the parent's constraint and makes it more +specific. + +A subtype can also define its own constraint failure message. This +lets you do things like have an error "The value you provided (20), +was not a valid rating, which must be a number from 1-10." This is +much friendlier than the default error, which just says that the value +failed a validation check for the type. + +Here's a simple (and useful) subtype example: + + subtype 'PositiveInt' + => as 'Int' + => where { $_ > 0 } + => message { "The number you provided, $_, was not a positive number" } + +Note that the sugar functions for working with types are all exported +by L. + +=head2 Creating a New Type (That Isn't a Subtype) + +You can also create new top-level types: + + type 'FourCharacters' => where { defined $_ && length $_ == 4 }; + +In practice, this example is pretty much the same as doing the same +thing as a subtype of C, except you have to check defined-ness +yourself. + +It's hard to find a case where you wouldn't want to subtype a very +broad type like C, C or C. + +In practice, defining a new top-level type is conceptually the same as +subtyping C. + +=head1 TYPE NAMES + +Type names are global throughout the current Perl +interpreter. Internally, Moose maps names to type objects via +L singleton. + +If you have multiple apps or libraries all using Moose in the same +process, you could have problems with collisions. We recommend that +you prefix names with some sort of namespace indicator to prevent +these sorts of collisions. + +For example, instead of calling a type "PositiveInt", call it +"MyApp.Type.PositiveInt". + +Type names are just strings, and can contain any character you +want. We recommend that you I use "::" as a separator in type +names. This can be very confusing, because class names are I +valid type names! Using something else, like a period, makes it clear +that "MyApp::User" is a class and "MyApp.Type.PositiveInt" is a Moose +type defined by your application. + +The C module lets you create bareword aliases to longer +names (really, the barewords are functions). + +=head1 COERCION + +One of the most powerful features of Moose's type system is its +coercions. A coercion is a mapping between two types. + + subtype 'ArrayRefOfInts' + => as 'ArrayRef[Int]'; + + coerce 'ArrayRefOfInts' + => from 'Int' + => via { [ $_ ] }; + +You'll note that we had to create a subtype rather than coercing +C directly. This is just a quirk of how Moose +works. + +Coercions, like type names, are global. This is I reason why +it is good to namespace your types. Moose will I try to coerce +a value unless you explicitly ask for it. This is done by setting the +C attribute parameter to a true value: + + package Foo; + + has 'sizes' => ( + is => 'rw', + isa => 'ArrayRefOfInts', + coerce => 1, + ); + + Foo->new( sizes => 42 ); + +This code example will do the right thing, and the newly created +object will have C<[ 42 ]> as its C attribute. + +=head2 Deep Coercion + +Deep coercion is the coercion of type parameters for parameterized +types. Let's take these types as an example: + + subtype 'HexNum' + => as 'Str' + => where { /[a-f0-9]/i }; + + coerce 'Int' + => from 'HexNum' + => via { hex $_ }; + + has 'sizes' => ( + is => 'rw', + isa => 'ArrayRef[Int]', + coerce => 1, + ); + +If we try passing an array reference of hex numbers for the C +attribute, Moose will not do any coercion. The reason for this is that +it gets very complicate very fast. + +However, if you want to, you can define a set of subtypes to enable +coercion between two parameterized types. + + subtype 'ArrayRefOfHexNums' + => as 'ArrayRef[HexNum]'; + + subtype 'ArrayRefOfInts' + => as 'ArrayRef[Int]'; + + coerce 'ArrayRefOfInts' + => from 'ArrayRefOfHexNums' + => via { [ map { hex } @{$_} ] }; + + Foo->new( sizes => [ 'a1', 'ff', '22' ] ); + +Now Moose will coerce the hex numbers to integers. + +However, Moose does not attempt to chain coercions, so we cannot pass +a single hex number. If we want to make that possible as well, we need +to define yet another coercion: + + coerce 'ArrayRefOfInts' + => from 'HexNum' + => via { [ hex $_ ] }; + +Yes, this can all get verbose, but coercion is tricky magic, and we +think it's best to make it as explicit as possible. + +=head1 TYPE UNIONS + +Moose allows you to say that an attribute can be of two or more +disparate types. For example, we might allow an C or +C: + + has 'output' => ( + is => 'rw', + isa => 'Object | FileHandle', + ); + +Moose actually parses that string and recognizes that you are creating +a type union. The C attribute will accept any sort of object, +as well as an unblessed file handle. It is up to you to do the right +thing for each of them in your code. + +Whenever you consider using a type union, you should think about +whether or not coercion might be a better answer. + +For our example above, we might want to be more specific, and insist +that output be an object with a C method: + + subtype 'CanPrint' + => as 'Object' + => where { $_->can('print') }; + +We can coerce file handles to an object that satisfies this condition +with a simple wrapper class: + + package FHWrapper; + + use Moose; + + has 'handle' => ( + is => 'ro', + isa => 'FileHandle', + ); + + sub print { + my $self = shift; + my $fh = $self->handle(); + + print $fh @_; + } + +Now we can define a coercion from C to our wrapper class: + + coerce 'FHWrapper' + => from 'FileHandle' + => via { FHWrapper->new( handle => $_ ) }; + + has 'output' => ( + is => 'rw', + isa => 'CanPrint', + coerce => 1, + ); + +This pattern, using a coercion instead of a type union, can help +simplify the use of the attribute, and should be considered whenever +you have a type union. + +=head1 TYPE CREATION HELPERS + +The L module exports a number of helper +functions for creating specific kinds of types. These include +C, C, and C. See the docs for +details. + +One helper worth noting is C, which allows you to create a +subtype of C that only allows the specified values: + + enum 'RGB' => qw( red green blue ); + +This creates a type named C + +=head1 ANONYMOUS TYPES + +All of the type creation functions return a type object. This type +object can be used wherever you would use a type name, as a parent +type, or as the value for an attribute's C parameter: + + has 'size' => ( + is => 'rw', + isa => subtype 'Int' => where { $_ > 0 }, + ); + +This is handy when you want to create a one-off type and don't want to +"pollute" the global namespace registry. + +=head1 VALIDATING METHOD PARAMETERS + +Moose does not provide any means of validating method +parameters. However, there are several MooseX extensions on CPAN which +let you do this. + +The simplest and least sugary is C. This +lets you validate a set of named parameters using Moose types: + + use Moose; + use MooseX::Params::Validate; + + sub foo { + my $self = shift; + my %params = validate( + \@_, + bar => { isa => 'Str', default => 'Moose' }, + ); + ... + } + +C also supports coercions. + +There are several more powerful extensions that support method +parameter validation using Moose types, including +C, which gives you a full-blown C +keyword. + + method morning (Str $name) { + $self->say("Good morning ${name}!"); + } + +=head1 AUTHOR + +Dave Rolsky Eautarch@urth.orgE + +=head1 COPYRIGHT AND LICENSE + +Copyright 2008 by Infinity Interactive, Inc. + +L + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut