maybe a real fix for coercions?
[gitmo/MooseX-Dependent.git] / README
CommitLineData
c08b66ad 1NAME
2 MooseX::Types::Parameterizable - Create your own Parameterizable Types.
3
4SYNOPSIS
5 Within your MooseX::Types declared library module:
6
7 use MooseX::Types::Parameterizable qw(Parameterizable);
6c0e3459 8
9 subtype Set,
10 as class_type("Set::Scalar");
c08b66ad 11
12 subtype UniqueInt,
13 as Parameterizable[Int, Set],
14 where {
15 my ($int, $set) = @_;
16 return !$set->has($int);
17 };
c08b66ad 18
6c0e3459 19 subtype PositiveSet,
20 as Set,
21 where {
22 my ($set) = @_;
23 return !grep {$_ <0 } $set->members;
24 };
c08b66ad 25
6c0e3459 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
c08b66ad 42
43DESCRIPTION
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
6c0e3459 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
c08b66ad 71
72 This throws a hard Moose exception. You'll need to capture it in an eval
6c0e3459 73 or related exception catching system (see TryCatch or <Try::Tiny>.)
c08b66ad 74
6c0e3459 75 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
c08b66ad 76
77 If you can't accept a hard exception here, you'll need to test the
78 constraining values first, as in:
79
6c0e3459 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 }
c08b66ad 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
6c0e3459 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!
c08b66ad 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
6c0e3459 103 TypeConstraint([$ob])->method;
c08b66ad 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
6c0e3459 117 subtype RangedInt,
118 as Parameterizable[Int, Range],
119 where {
120 my ($value, $range) = @_;
121 return ($value >= $range->{min} &&
122 $value =< $range->{max});
123 };
c08b66ad 124
125 Example subtype with additional constraints:
126
6c0e3459 127 subtype PositiveRangedInt,
128 as RangedInt,
129 where {
130 shift >= 0;
131 };
c08b66ad 132
133 Or you could have done the following instead:
134
6c0e3459 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];
c08b66ad 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
6c0e3459 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 ];
c08b66ad 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
6c0e3459 184 subtype RequiredAgeInYears,
185 as Int;
c08b66ad 186
6c0e3459 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 }
c08b66ad 193
194 This would validate the following:
195
6c0e3459 196 my $person = Person->new(age=>35);
197 PersonOverAge([18])->check($person);
c08b66ad 198
199 You can then apply the following coercion
200
6c0e3459 201 coerce PersonOverAge,
202 from Dict[age=>int],
203 via {Person->new(%$_)},
204 from Int,
205 via {Person->new(age=>$_)};
c08b66ad 206
207 This coercion would then apply to all the following:
208
6c0e3459 209 PersonOverAge([18])->check(30); ## via the Int coercion
210 PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
c08b66ad 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
6c0e3459 216 coerce PersonOverAge[18],
217 from DateTime,
218 via {$_->years};
c08b66ad 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
6c0e3459 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 };
c08b66ad 242
243 Recursion
6c0e3459 244 TBD
c08b66ad 245
246TYPE 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
259AUTHOR
260 John Napiorkowski, "<jjnapiork@cpan.org>"
261
262COPYRIGHT & LICENSE
263 This program is free software; you can redistribute it and/or modify it
264 under the same terms as Perl itself.
265