Merge pull request #3 from brianphillips/master
[gitmo/MooseX-Dependent.git] / README
diff --git a/README b/README
index 4f5099d..fffa7e6 100644 (file)
--- a/README
+++ b/README
@@ -11,9 +11,7 @@ SYNOPSIS
         use MooseX::Types::Moose qw(Str Int ArrayRef);
         use MooseX::Types -declare=>[qw(Varchar)];
 
-        ## Create a type constraint that is a string but parameterizes an integer
-        ## that is used as a maximum length constraint on that string, similar to
-        ## an SQL Varchar type.
+    Create a type constraint that is similar to SQL Varchar type.
 
         subtype Varchar,
           as Parameterizable[Str,Int],
@@ -23,6 +21,8 @@ SYNOPSIS
           },
           message { "'$_' is too long"  };
 
+    Coerce an ArrayRef to a string via concatenation.
+
         coerce Varchar,
           from ArrayRef,
           via { 
@@ -32,22 +32,25 @@ SYNOPSIS
 
         has 'varchar_five' => (isa=>Varchar[5], is=>'ro', coerce=>1);
         has 'varchar_ten' => (isa=>Varchar[10], is=>'ro');
-  
-        ## Object created since attributes are valid
+
+    Object created since attributes are valid
+
         my $object1 = __PACKAGE__->new(
             varchar_five => '1234',
             varchar_ten => '123456789',
         );
 
-        ## Dies with an invalid constraint for 'varchar_five'
+    Dies with an invalid constraint for 'varchar_five'
+
         my $object2 = __PACKAGE__->new(
-            varchar_five => '12345678',
+            varchar_five => '12345678',  ## too long!
             varchar_ten => '123456789',
         );
 
-        ## varchar_five coerces as expected
+    varchar_five coerces as expected
+
         my $object3 = __PACKAGE__->new(
-            varchar_five => [qw/aa bb/],
+            varchar_five => [qw/aa bb/],  ## coerces to "aabb"
             varchar_ten => '123456789',
         );
 
@@ -56,13 +59,20 @@ SYNOPSIS
 DESCRIPTION
     A MooseX::Types library for creating parameterizable types. A
     parameterizable type constraint for all intents and uses is a subclass
-    of a parent type, but adds a secondary type parameter which is available
-    to constraint callbacks (such as inside the 'where' clause) or in the
-    coercions.
+    of a parent type, but adds additional type parameters which are
+    available to constraint callbacks (such as inside the 'where' clause of
+    a type constraint definition) or in the coercions you define for a given
+    type constraint.
 
-    This allows you to create a type that has additional runtime advice,
-    such as a set of numbers within which another number must be unique, or
-    allowable ranges for a integer, such as in:
+    If you have Moose experience, you probably are familiar with the builtin
+    parameterizable type constraints 'ArrayRef' and 'HashRef'. This type
+    constraint lets you generate your own versions of parameterized
+    constraints that work similarly. See Moose::Util::TypeConstraints for
+    more.
+
+    Using this type constraint, you can generate new type constraints that
+    have additional runtime advice, such as being able to specify maximum
+    and minimum values for an Int (integer) type constraint:
 
         subtype Range,
             as Dict[max=>Int, min=>Int],
@@ -80,15 +90,30 @@ DESCRIPTION
             };
         
         RangedInt([{min=>10,max=>100}])->check(50); ## OK
-        RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
+        RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, exceeds max
+
+    This is useful since it lets you generate common patterns of type
+    constraints rather than build a custom type constraint for all similar
+    cases.
 
-    This throws a hard Moose exception. You'll need to capture it in an eval
-    or related exception catching system (see TryCatch or <Try::Tiny>.)
+    The type parameter must be valid against the 'constrainting' type
+    constraint used in the Parameterizable condition. If you pass an invalid
+    value this throws a hard Moose exception. You'll need to capture it in
+    an eval or related exception catching system (see TryCatch or
+    Try::Tiny.)
+
+    For example the following would throw a hard error (and not just return
+    false)
 
         RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
 
-    If you can't accept a hard exception here, you'll need to test the
-    constraining values first, as in:
+    In the above case the 'min' value is larger than the 'max', which
+    violates the Range constraint. We throw a hard error here since I think
+    incorrect type parameters are most likely to be the result of a typo or
+    other true error conditions.
+
+    If you can't accept a hard exception here, you can either trap it as
+    advised above or you need to test the constraining values first, as in:
 
         my $range = {min=>99, max=>10};
         if(my $err = Range->validate($range)) {
@@ -103,25 +128,23 @@ DESCRIPTION
     written as:
 
         RangedInt([min=>10,max=>100])->check(50); ## OK
-        RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
-        RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
+        RangedInt([min=>50, max=>75])->check(99); ## Not OK, exceeds max
+        RangedInt([min=>99, max=>10])->check(10); ## Exception, not valid Range
 
     This is the preferred syntax, as it improve readability and adds to the
-    conciseness of your type constraint declarations. An exception wil be
-    thrown if your type parameters don't match the required reference type.
+    conciseness of your type constraint declarations.
 
-    Also not that if you 'chain' parameterization results with a method call
-    like:
+    Also note that if you 'chain' parameterization results with a method
+    call like:
 
         TypeConstraint([$ob])->method;
 
     You need to have the "(...)" around the ArrayRef in the Type Constraint
-    parameters. This seems to have something to do with the precendent level
-    of "->". Patches or thoughts welcomed. You only need to do this in the
-    above case which I imagine is not a very common case.
-
-    ==head2 Subtyping a Parameterizable type constraints
+    parameters. You can skip the wrapping parenthesis in the most common
+    cases, such as when you use the type constraint in the options section
+    of a Moose attribute declaration, or when defining type libraries.
 
+  Subtyping a Parameterizable type constraints
     When subclassing a parameterizable type you must be careful to match
     either the required type parameter type constraint, or if
     re-parameterizing, the new type constraints are a subtype of the parent.
@@ -143,7 +166,18 @@ DESCRIPTION
                 shift >= 0;              
             };
 
-    Or you could have done the following instead:
+    In this case you'd now have a parameterizable type constraint which
+    would work like:
+
+        Test::More::ok PositiveRangedInt([{min=>-10, max=>75}])->check(5);
+        Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5);
+
+    Of course the above is somewhat counter-intuitive to the reader, since
+    we have defined our 'RangedInt' in such as way as to let you declare
+    negative ranges. For the moment each type constraint rule is apply
+    without knowledge of any other rule, nor can a rule 'inform' existing
+    rules. This is a limitation of the current system. However, you could
+    instead do the following:
 
         ## Subtype of Int for positive numbers
         subtype PositiveInt,
@@ -161,113 +195,126 @@ DESCRIPTION
         subtype PositiveRangedInt,
             as RangedInt[PositiveRange];
 
+    This would constrain values in the same way as the previous type
+    constraint but have the bonus that you'd throw a hard exception if you
+    try to use an incorrect range:
+
+        Test::More::ok PositiveRangedInt([{min=>10, max=>75}])->check(15); ## OK
+        Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5); ## Dies
+
     Notice how re-parameterizing the parameterizable type 'RangedInt' works
     slightly differently from re-parameterizing 'PositiveRange' Although it
     initially takes two type constraint values to declare a parameterizable
     type, should you wish to later re-parameterize it, you only use a
-    subtype of the second type parameter (the parameterizable type
-    constraint) since the first type constraint sets the parent type for the
-    parameterizable type. In other words, given the example above, a type
-    constraint of 'RangedInt' would have a parent of 'Int', not
-    'Parameterizable' and for all intends and uses you could stick it
-    wherever you'd need an Int.
-
-        subtype NameAge,
-            as Tuple[Str, Int];
-    
-        ## re-parameterized subtypes of NameAge containing a Parameterizable Int    
-        subtype NameBetween18and35Age,
-            as NameAge[
-                Str,
-                PositiveRangedInt[min=>18,max=>35],
-            ];
-
-    One caveat is that you can't stick an unparameterized parameterizable
-    type inside a structure, such as MooseX::Types::Structured since that
-    would require the ability to convert a 'containing' type constraint into
-    a parameterizable type, which is a capacity we current don't have.
+    subtype of the extra type parameter (the parameterizable type
+    constraints) since the first type constraint sets the parent type for
+    the parameterizable type.
 
-  Coercions
-    Parameterizable types have some limited support for coercions. Several
-    things must be kept in mind. The first is that the coercion targets the
-    type constraint which is being made parameterizable, Not the
-    parameterizable type. So for example if you create a Parameterizable
-    type like:
+    In other words, given the example above, a type constraint of
+    'RangedInt' would have a parent of 'Int', not 'Parameterizable' and for
+    all intends and uses you could stick it wherever you'd need an Int. You
+    can't change the parent, even to make it a subclass of Int.
 
-        subtype RequiredAgeInYears,
-          as Int;
+  Coercions
+    A type coercion is a rule that allows you to transform one type from one
+    or more other types. Please see Moose::Cookbook::Basics::Recipe5 for an
+    example of type coercions if you are not familiar with the subject.
+
+    MooseX::Types::Parameterizable supports type coercions in all the ways
+    you would expect. In addition, it also supports a limited form of type
+    coercion inheritance. Generally speaking, type constraints don't inherit
+    coercions since this would rapidly become confusing. However, since your
+    parameterizable type is intended to become parameterized in order to be
+    useful, we support inheriting from a 'base' parameterizable type
+    constraint to its 'child' parameterized sub types.
+
+    For the purposes of this discussion, a parameterizable type is a subtype
+    created when you say, "as Parameterizable[..." in your sub type
+    declaration. For example:
 
-        subtype PersonOverAge,
-          as Parameterizable[Person, RequiredAgeInYears]
+        subtype Varchar,
+          as Parameterizable[Str, Int],
           where {
-            my ($person, $required_years_old) = @_;
-            return $person->years_old > $required_years_old;
-          }
+            my($string, $int) = @_;
+            $int >= length($string) ? 1:0;
+          },
+          message { "'$_' is too long"  };
 
-    This would validate the following:
+    This is the "SYNOPSIS" example, which creates a new parameterizable
+    subtype of Str which takes a single type parameter which must be an Int.
+    This Int is used to constrain the allowed length of the Str value.
 
-        my $person = Person->new(age=>35);
-        PersonOverAge([18])->check($person);
+    Now, this new sub type, "Varchar", is parameterizable since it can take
+    a type parameter. We can apply some coercions to it:
 
-    You can then apply the following coercion
+        coerce Varchar,
+          from Object,
+          via { "$_"; },  ## stringify the object
+          from ArrayRef,
+          via { join '',@$_ };  ## convert array to string
 
-        coerce PersonOverAge,
-          from Dict[age=>int],
-          via {Person->new(%$_)},
-          from Int,
-          via {Person->new(age=>$_)};
+    This parameterizable subtype, "Varchar" itself is something you'd never
+    use directly to constraint a value. In other words you'd never do
+    something like:
 
-    This coercion would then apply to all the following:
+        has name => (isa=>Varchar, ...); ## Why not just use a Str?
 
-        PersonOverAge([18])->check(30); ## via the Int coercion
-        PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
+    You are going to do this:
 
-    However, you are not allowed to place coercions on parameterizable types
-    that have had their constraining value filled, nor subtypes of such. For
-    example:
+        has name => (isa=>Varchar[40], ...)
 
-        coerce PersonOverAge[18],
-          from DateTime,
-          via {$_->years};
+    Which is actually useful. However, "Varchar[40]" is a parameterized
+    type, it is a subtype of the parameterizable "Varchar" and it inherits
+    coercions from its parent. This may be a bit surprising to Moose
+    developers, but I believe this is the actual desired behavior.
 
-    That would generate a hard exception. This is a limitation for now until
-    I can devise a smarter way to cache the generated type constraints.
-    However, I doubt it will be a significant limitation, since the general
-    use case is supported.
+    You can of course add new coercions to a subtype of a parameterizable
+    type:
 
-    Lastly, the constraining value is available in the coercion in much the
-    same way it is available to the constraint.
+        subtype MySpecialVarchar,
+          as Varchar;
 
-        ## Create a type constraint where a Person must be in the set
-        subtype PersonInSet,
-            as Parameterizable[Person, PersonSet],
-            where {
-                my ($person, $person_set) = @_;
-                $person_set->find($person);
-            }
-
-        coerce PersonInSet,
-            from HashRef,
-            via {
-                my ($hashref, $person_set) = @_;
-                return $person_set->create($hash_ref);
-            };
+        coerce MySpecialVarchar,
+          from ...
+
+    In which case this new parameterizable type would NOT inherit coercions
+    from it's parent parameterizable type (Varchar). This is done in keeping
+    with how generally speaking Moose type constraints avoid complicated
+    coercion inheritance schemes, however I am open to discussion if there
+    are valid use cases.
+
+    NOTE: One thing you can't do is add a coercion to an already
+    parameterized type. Currently the following would throw a hard error:
+
+        subtype 40CharStr,
+          as Varchar[40];
+
+        coerce 40CharStr, ...  # BANG!
+
+    This limitation is enforced since generally we expect coercions on the
+    parent. However if good use cases arise we may lift this in the future.
+
+    In general we are trying to take a conservative approach that keeps in
+    line with how most Moose authors expect type constraints to work.
 
   Recursion
-        TBD
+        TBD - Needs a use case... Anyone?
 
 TYPE CONSTRAINTS
     This type library defines the following constraints.
 
-  Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
+  Parameterizable[ParentTypeConstraint, ConstrainingValueTypeConstraint]
     Create a subtype of ParentTypeConstraint with a dependency on a value
-    that can pass the ParameterizableValueTypeConstraint. If
-    ParameterizableValueTypeConstraint is empty we default to the 'Any' type
-    constraint (see Moose::Util::TypeConstraints).
+    that can pass the ConstrainingValueTypeConstraint. If
+    ConstrainingValueTypeConstraint is empty we default to the 'Any' type
+    constraint (see Moose::Util::TypeConstraints). This is useful if you are
+    creating some base Parameterizable type constraints that you intend to
+    sub class.
+
+SEE ALSO
+    The following modules or resources may be of interest.
 
-    This creates a type constraint which must be further parameterized at
-    later time before it can be used to ->check or ->validate a value.
-    Attempting to do so will cause an exception.
+    Moose, Moose::Meta::TypeConstraint, MooseX::Types
 
 AUTHOR
     John Napiorkowski, "<jjnapiork@cpan.org>"