more tabs to spaces
[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);
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
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
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
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