fixed up the coercion stuff, got something that should give us 80%+ what we need
[gitmo/MooseX-Dependent.git] / lib / MooseX / Dependent / Types.pm
index b013b5c..c08f822 100644 (file)
@@ -20,7 +20,7 @@ Within your L<MooseX::Types> declared library module:
             my ($int, $set) = @_;
             return $set->find($int) ? 0:1;
         };
-
+               
 =head1 DESCRIPTION
 
 A L<MooseX::Types> library for creating dependent types.  A dependent type
@@ -44,25 +44,48 @@ for a integer, such as in:
                where {
                        my ($value, $range) = @_;
                        return ($value >= $range->{min} &&
-                        $value =< $range->{max});
+                        $value <= $range->{max});
                };
                
-       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); ## Not OK, not a valid Range!
+       RangedInt([{min=>10,max=>100}])->check(50); ## OK
+       RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
+       
+This throws a hard Moose exception.  You'll need to capture it in an eval or
+related exception catching system (see L<TryCatch>).
+
+       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:
+
+       my $range = {min=>99, max=>10};
+       if(my $err = Range->validate($range)) {
+               ## Handle #$err
+       } else {
+               RangedInt($range)->check(99);
+       }
        
 Please note that for ArrayRef or HashRef dependent type constraints, as in the
 example above, as a convenience we automatically ref the incoming type
 parameters, so that the above could also be 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); ## Not OK, not a valid Range!
+       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!
 
 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.
 
+Also not 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 Dependent type constraints
 
 When subclassing a dependent type you must be careful to match either the
@@ -91,19 +114,20 @@ Or you could have done the following instead (example of re-paramterizing)
        subtype PositiveInt,
                as Int,
                where {
-                       shift >= 0;
+                       my ($value, $range) = @_;
+                       return $value >= 0;
                };
 
        ## subtype Range to re-parameterize Range with subtypes
-       subtype PositveRange,
+       subtype PositiveRange,
                as Range[max=>PositiveInt, min=>PositiveInt];
        
        ## create subtype via reparameterizing
        subtype PositiveRangedInt,
-               as RangedInt[PositveRange];
+               as RangedInt[PositiveRange];
 
 Notice how re-parameterizing the dependent type 'RangedInt' works slightly
-differently from re-parameterizing 'PositiveRange'?  Although it initially takes
+differently from re-parameterizing 'PositiveRange'  Although it initially takes
 two type constraint values to declare a dependent type, should you wish to
 later re-parameterize it, you only use a subtype of the second type parameter
 (the dependent type constraint) since the first type constraint sets the parent
@@ -128,55 +152,71 @@ is a capacity we current don't have.
        
 =head2 Coercions
 
-You can place coercions on dependent types, however you need to pay attention to
-what you are actually coercion, the unparameterized or parameterized constraint.
-
-    TBD: Need discussion and example of coercions working for both the
-    constrainted and dependent type constraint.
-       
-       subtype OlderThanAge,
-               as Dependent[Int, Dict[older_than=>Int]],
-               where {
-                       my ($value, $dict) = @_;
-                       return $value > $dict->{older_than} ? 1:0;
-               };
+Dependent 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 dependent, Not the dependent type.  So for example if you
+create a Dependent type like:
 
-Which should work like:
+       subtype RequiredAgeInYears,
+         as Int;
 
-       OlderThanAge[{older_than=>25}]->check(39); ## is OK
-       OlderThanAge[older_than=>1]->check(9); ## OK, using reference type inference
+       subtype PersonOverAge,
+         as Dependent[Person, RequiredAgeInYears]
+         where {
+               my ($person, $required_years_old) = @_;
+               return $person->years_old > $required_years_old;
+         }
 
-And you can create coercions like:
+This would validate the following:
+       
+       my $person = Person->new(age=>35);
+       PersonOverAge([18])->check($person);
+       
+You can then apply the following coercion
+
+       coerce PersonOverAge,
+         from Dict[age=>int],
+         via {Person->new(%$_)},
+         from Int,
+         via {Person->new(age=>$_)};
+         
+This coercion would then apply to all the following:
+
+       PersonOverAge([18])->check(30); ## via the Int coercion
+       PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
+
+However, you are not allowed to place coercions on dependent types that have
+had their constraining value filled, nor subtypes of such.  For example:
+
+       coerce PersonOverAge[18],
+         from DateTime,
+         via {$_->years};
+         
+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.
+
+Lastly, the constraining value is available in the coercion in much the same way
+it is available to the constraint.
+
+       ## Create a type constraint where a Person must be in the set
+       subtype PersonInSet,
+               as Dependent[Person, PersonSet],
+               where {
+                       my ($person, $person_set) = @_;
+                       $person_set->find($person);
+               }
 
-       coerce OlderThanAge,
-               from Tuple[Int, Int],
+       coerce PersonInSet,
+               from HashRef,
                via {
-                       my ($int, $int);
-                       return [$int, {older_than=>$int}];
+                       my ($hashref, $person_set) = @_;
+                       return $person_set->create($hash_ref);
                };
 
 =head2 Recursion
 
-Newer versions of L<MooseX::Types> support recursive type constraints.  That is
-you can include a type constraint as a contained type constraint of itself.
-Recursion is support in both the dependent and constraining type constraint. For
-example, if we assume an Object hierarchy like Food -> [Grass, Meat]
-       
-       TODO: DOES THIS EXAMPLE MAKE SENSE?
-       
-    subtype Food,
-               as Dependent[Food, Food],
-               where {
-                       my ($value, $allowed_food_type) = @_;
-                       return $value->isa($allowed_food_type);
-               };
-       
-       my $grass = Food::Grass->new;
-       my $meat = Food::Meat->new;
-       my $vegetarian = Food[$grass];
-       
-       $vegetarian->check($grass); ## Grass is the allowed food of a vegetarian
-       $vegetarian->check($meat); ## BANG, vegetarian can't eat meat!
+       TBD
 
 =head1 TYPE CONSTRAINTS
 
@@ -214,40 +254,3 @@ it under the same terms as Perl itself.
 =cut
 
 1;
-
-__END__
-
-oose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
-    Moose::Meta::TypeConstraint::Parameterizable->new(
-        name => 'MooseX::Dependent::Types::Dependent',
-        parent => find_type_constraint('Any'),
-               constraint => sub { 0 },
-        constraint_generator=> sub { 
-                       my ($dependent_val, $callback, $constraining_val) = @_;
-                       return $callback->($dependent_val, $constraining_val);
-        },
-    )
-);
-
-
-
-$REGISTRY->add_type_constraint(
-    Moose::Meta::TypeConstraint::Parameterizable->new(
-        name               => 'HashRef',
-        package_defined_in => __PACKAGE__,
-        parent             => find_type_constraint('Ref'),
-        constraint         => sub { ref($_) eq 'HASH' },
-        optimized =>
-            \&Moose::Util::TypeConstraints::OptimizedConstraints::HashRef,
-        constraint_generator => sub {
-            my $type_parameter = shift;
-            my $check          = $type_parameter->_compiled_type_constraint;
-            return sub {
-                foreach my $x ( values %$_ ) {
-                    ( $check->($x) ) || return;
-                }
-                1;
-                }
-        }
-    )
-);
\ No newline at end of file