added readme
[gitmo/MooseX-Dependent.git] / README
1 NAME
2     MooseX::Types::Parameterizable - Create your own Parameterizable Types.
3
4 SYNOPSIS
5     Within your MooseX::Types declared library module:
6
7         use MooseX::Types::Parameterizable qw(Parameterizable);
8         
9             subtype Set,
10                     as class_type("Set::Scalar");
11
12         subtype UniqueInt,
13             as Parameterizable[Int, Set],
14             where {
15                 my ($int, $set) = @_;
16                 return !$set->has($int);
17             };
18                 
19             subtype PositiveSet,
20                     as Set,
21                     where {
22                             my ($set) = @_;
23                             return !grep {$_ <0 } $set->members;
24                     };
25                 
26         subtype PositiveUniqueInt,
27             as UniqueInt[PositiveSet];
28         
29             my $set = Set::Scalar->new(1,2,3);
30
31             UniqueInt([$set])->check(100);  ## Okay, 100 isn't in (1,2,3)
32             UniqueInt([$set])->check(-99);  ## Okay, -99 isn't in (1,2,3)
33             UniqueInt([$set])->check(2);  ## Not OK, 2 is in (1,2,3)
34         
35             PositiveUniqueInt([$set])->check(100);  ## Okay, 100 isn't in (1,2,3)
36             PositiveUniqueInt([$set])->check(-99);  ## Not OK, -99 not Positive Int
37             PositiveUniqueInt([$set])->check(2);  ## Not OK, 2 is in (1,2,3)
38         
39             my $negative_set = Set::Scalar->new(-1,-2,-3);
40         
41             UniqueInt([$negative_set])->check(100);  ## Throws exception
42
43 DESCRIPTION
44     A MooseX::Types library for creating parameterizable types. A
45     parameterizable type constraint for all intents and uses is a subclass
46     of a parent type, but adds a secondary type parameter which is available
47     to constraint callbacks (such as inside the 'where' clause) or in the
48     coercions.
49
50     This allows you to create a type that has additional runtime advice,
51     such as a set of numbers within which another number must be unique, or
52     allowable ranges for a integer, such as in:
53
54             subtype Range,
55                     as Dict[max=>Int, min=>Int],
56                     where {
57                             my ($range) = @_;
58                             return $range->{max} > $range->{min};
59                     };
60
61             subtype RangedInt,
62                     as Parameterizable[Int, Range],
63                     where {
64                             my ($value, $range) = @_;
65                             return ($value >= $range->{min} &&
66                              $value <= $range->{max});
67                     };
68                 
69             RangedInt([{min=>10,max=>100}])->check(50); ## OK
70             RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
71
72     This throws a hard Moose exception. You'll need to capture it in an eval
73     or related exception catching system (see TryCatch).
74
75             RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
76
77     If you can't accept a hard exception here, you'll need to test the
78     constraining values first, as in:
79
80             my $range = {min=>99, max=>10};
81             if(my $err = Range->validate($range)) {
82                     ## Handle #$err
83             } else {
84                     RangedInt($range)->check(99);
85             }
86
87     Please note that for ArrayRef or HashRef parameterizable type
88     constraints, as in the example above, as a convenience we automatically
89     ref the incoming type parameters, so that the above could also be
90     written as:
91
92             RangedInt([min=>10,max=>100])->check(50); ## OK
93             RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
94             RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
95
96     This is the preferred syntax, as it improve readability and adds to the
97     conciseness of your type constraint declarations. An exception wil be
98     thrown if your type parameters don't match the required reference type.
99
100     Also not that if you 'chain' parameterization results with a method call
101     like:
102
103             TypeConstraint([$ob])->method;
104
105     You need to have the "(...)" around the ArrayRef in the Type Constraint
106     parameters. This seems to have something to do with the precendent level
107     of "->". Patches or thoughts welcomed. You only need to do this in the
108     above case which I imagine is not a very common case.
109
110     ==head2 Subtyping a Parameterizable type constraints
111
112     When subclassing a parameterizable type you must be careful to match
113     either the required type parameter type constraint, or if
114     re-parameterizing, the new type constraints are a subtype of the parent.
115     For example:
116
117             subtype RangedInt,
118                     as Parameterizable[Int, Range],
119                     where {
120                             my ($value, $range) = @_;
121                             return ($value >= $range->{min} &&
122                              $value =< $range->{max});
123                     };
124
125     Example subtype with additional constraints:
126
127             subtype PositiveRangedInt,
128                     as RangedInt,
129                     where {
130                             shift >= 0;                     
131                     };
132
133     Or you could have done the following instead:
134
135             ## Subtype of Int for positive numbers
136             subtype PositiveInt,
137                     as Int,
138                     where {
139                             my ($value, $range) = @_;
140                             return $value >= 0;
141                     };
142
143             ## subtype Range to re-parameterize Range with subtypes
144             subtype PositiveRange,
145                     as Range[max=>PositiveInt, min=>PositiveInt];
146         
147             ## create subtype via reparameterizing
148             subtype PositiveRangedInt,
149                     as RangedInt[PositiveRange];
150
151     Notice how re-parameterizing the parameterizable type 'RangedInt' works
152     slightly differently from re-parameterizing 'PositiveRange' Although it
153     initially takes two type constraint values to declare a parameterizable
154     type, should you wish to later re-parameterize it, you only use a
155     subtype of the second type parameter (the parameterizable type
156     constraint) since the first type constraint sets the parent type for the
157     parameterizable type. In other words, given the example above, a type
158     constraint of 'RangedInt' would have a parent of 'Int', not
159     'Parameterizable' and for all intends and uses you could stick it
160     wherever you'd need an Int.
161
162             subtype NameAge,
163                     as Tuple[Str, Int];
164         
165             ## re-parameterized subtypes of NameAge containing a Parameterizable Int        
166             subtype NameBetween18and35Age,
167                     as NameAge[
168                             Str,
169                             PositiveRangedInt[min=>18,max=>35],
170                     ];
171
172     One caveat is that you can't stick an unparameterized parameterizable
173     type inside a structure, such as MooseX::Types::Structured since that
174     would require the ability to convert a 'containing' type constraint into
175     a parameterizable type, which is a capacity we current don't have.
176
177   Coercions
178     Parameterizable types have some limited support for coercions. Several
179     things must be kept in mind. The first is that the coercion targets the
180     type constraint which is being made parameterizable, Not the
181     parameterizable type. So for example if you create a Parameterizable
182     type like:
183
184             subtype RequiredAgeInYears,
185               as Int;
186
187             subtype PersonOverAge,
188               as Parameterizable[Person, RequiredAgeInYears]
189               where {
190                     my ($person, $required_years_old) = @_;
191                     return $person->years_old > $required_years_old;
192               }
193
194     This would validate the following:
195
196             my $person = Person->new(age=>35);
197             PersonOverAge([18])->check($person);
198
199     You can then apply the following coercion
200
201             coerce PersonOverAge,
202               from Dict[age=>int],
203               via {Person->new(%$_)},
204               from Int,
205               via {Person->new(age=>$_)};
206
207     This coercion would then apply to all the following:
208
209             PersonOverAge([18])->check(30); ## via the Int coercion
210             PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
211
212     However, you are not allowed to place coercions on parameterizable types
213     that have had their constraining value filled, nor subtypes of such. For
214     example:
215
216             coerce PersonOverAge[18],
217               from DateTime,
218               via {$_->years};
219
220     That would generate a hard exception. This is a limitation for now until
221     I can devise a smarter way to cache the generated type constraints.
222     However, I doubt it will be a significant limitation, since the general
223     use case is supported.
224
225     Lastly, the constraining value is available in the coercion in much the
226     same way it is available to the constraint.
227
228             ## Create a type constraint where a Person must be in the set
229             subtype PersonInSet,
230                     as Parameterizable[Person, PersonSet],
231                     where {
232                             my ($person, $person_set) = @_;
233                             $person_set->find($person);
234                     }
235
236             coerce PersonInSet,
237                     from HashRef,
238                     via {
239                             my ($hashref, $person_set) = @_;
240                             return $person_set->create($hash_ref);
241                     };
242
243   Recursion
244             TBD
245
246 TYPE CONSTRAINTS
247     This type library defines the following constraints.
248
249   Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
250     Create a subtype of ParentTypeConstraint with a dependency on a value
251     that can pass the ParameterizableValueTypeConstraint. If
252     ParameterizableValueTypeConstraint is empty we default to the 'Any' type
253     constraint (see Moose::Util::TypeConstraints).
254
255     This creates a type constraint which must be further parameterized at
256     later time before it can be used to ->check or ->validate a value.
257     Attempting to do so will cause an exception.
258
259 AUTHOR
260     John Napiorkowski, "<jjnapiork@cpan.org>"
261
262 COPYRIGHT & LICENSE
263     This program is free software; you can redistribute it and/or modify it
264     under the same terms as Perl itself.
265