[Moose-commits] r7414 - Moose/branches/moose-manual/lib/Moose/Manual

autarch at code2.0beta.co.uk autarch at code2.0beta.co.uk
Thu Jan 29 18:05:14 GMT 2009


Author: autarch
Date: 2009-01-29 10:05:14 -0800 (Thu, 29 Jan 2009)
New Revision: 7414

Added:
   Moose/branches/moose-manual/lib/Moose/Manual/Types.pod
Log:
Types manual page


Added: Moose/branches/moose-manual/lib/Moose/Manual/Types.pod
===================================================================
--- Moose/branches/moose-manual/lib/Moose/Manual/Types.pod	                        (rev 0)
+++ Moose/branches/moose-manual/lib/Moose/Manual/Types.pod	2009-01-29 18:05:14 UTC (rev 7414)
@@ -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<implicit> 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<Any> and C<Item> is
+conceptual. C<Item> is used as the top-level type in the hierarchy.
+
+The rest of these types correspond to existing Perl concepts. For
+example, a C<Num> is anything that Perl thinks looks like a number. An
+C<Object> is a blessed reference, etc.
+
+The types followed by "[`a]" can be parameterized. So instead of just
+plain C<ArrayRef> we can say that we want C<ArrayRef[Int]> instead. We
+can even do something like C<HashRef[ArrayRef[Str]]>.
+
+The C<Maybe[`a]> type deserves a special mention. Used by itself, it
+doesn't really mean anything (and is equivalent to C<Item>). When it
+is parameterized, it means that the value is either C<undef> or the
+parameterized type. So C<Maybe[Int]> means an integer or C<undef>
+
+For more details on the type hierarchy, see
+L<Moose::Util::TypeConstraints>.
+
+=head1 WHAT IS A TYPE?
+
+It's important to realize that types are not classes (or
+packages). Types are just objects (L<Moose::Meta::TypeConstraint>
+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<can be> 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<isa> 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<Int> is a child of
+C<Num> 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<all> 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<Moose::Util::TypeConstraints>.
+
+=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<Str>, 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<Defined>, C<Ref> or C<Object>.
+
+In practice, defining a new top-level type is conceptually the same as
+subtyping C<Item>.
+
+=head1 TYPE NAMES
+
+Type names are global throughout the current Perl
+interpreter. Internally, Moose maps names to type objects via
+L<Moose::Meta::TypeConstraint::Registry|registry> 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<do not> use "::" as a separator in type
+names. This can be very confusing, because class names are I<also>
+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<MooseX::Types> 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<ArrayRef[Int]> directly. This is just a quirk of how Moose
+works.
+
+Coercions, like type names, are global. This is I<another> reason why
+it is good to namespace your types. Moose will I<never> try to coerce
+a value unless you explicitly ask for it. This is done by setting the
+C<coerce> 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<sizes> 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<sizes>
+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<Object> or
+C<FileHandle>:
+
+  has 'output' => (
+      is  => 'rw',
+      isa => 'Object | FileHandle',
+  );
+
+Moose actually parses that string and recognizes that you are creating
+a type union. The C<output> 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<print> 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<FileHandle> 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<Moose::Util::TypeConstraints> module exports a number of helper
+functions for creating specific kinds of types. These include
+C<class_type>, C<role_type>, and C<maybe_type>. See the docs for
+details.
+
+One helper worth noting is C<enum>, which allows you to create a
+subtype of C<Str> that only allows the specified values:
+
+  enum 'RGB' => qw( red green blue );
+
+This creates a type named C<RGB>
+
+=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<isa> 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<MooseX::Params::Validate>. 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<MooseX::Params::Validate> also supports coercions.
+
+There are several more powerful extensions that support method
+parameter validation using Moose types, including
+C<MooseX::Method::Signatures>, which gives you a full-blown C<method>
+keyword.
+
+  method morning (Str $name) {
+      $self->say("Good morning ${name}!");
+  }
+
+=head1 AUTHOR
+
+Dave Rolsky E<lt>autarch at urth.orgE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2008 by Infinity Interactive, Inc.
+
+L<http://www.iinteractive.com>
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut




More information about the Moose-commits mailing list