2 MooseX::Types::Parameterizable - Create your own Parameterizable Types.
5 Within your MooseX::Types declared library module:
7 use MooseX::Types::Parameterizable qw(Parameterizable);
10 as class_type("Set::Scalar");
13 as Parameterizable[Int, Set],
16 return !$set->has($int);
23 return !grep {$_ <0 } $set->members;
26 subtype PositiveUniqueInt,
27 as UniqueInt[PositiveSet];
29 my $set = Set::Scalar->new(1,2,3);
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)
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)
39 my $negative_set = Set::Scalar->new(-1,-2,-3);
41 UniqueInt([$negative_set])->check(100); ## Throws exception
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
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:
55 as Dict[max=>Int, min=>Int],
58 return $range->{max} > $range->{min};
62 as Parameterizable[Int, Range],
64 my ($value, $range) = @_;
65 return ($value >= $range->{min} &&
66 $value <= $range->{max});
69 RangedInt([{min=>10,max=>100}])->check(50); ## OK
70 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
72 This throws a hard Moose exception. You'll need to capture it in an eval
73 or related exception catching system (see TryCatch).
75 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
77 If you can't accept a hard exception here, you'll need to test the
78 constraining values first, as in:
80 my $range = {min=>99, max=>10};
81 if(my $err = Range->validate($range)) {
84 RangedInt($range)->check(99);
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
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!
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.
100 Also not that if you 'chain' parameterization results with a method call
103 TypeConstraint([$ob])->method;
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.
110 ==head2 Subtyping a Parameterizable type constraints
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.
118 as Parameterizable[Int, Range],
120 my ($value, $range) = @_;
121 return ($value >= $range->{min} &&
122 $value =< $range->{max});
125 Example subtype with additional constraints:
127 subtype PositiveRangedInt,
133 Or you could have done the following instead:
135 ## Subtype of Int for positive numbers
139 my ($value, $range) = @_;
143 ## subtype Range to re-parameterize Range with subtypes
144 subtype PositiveRange,
145 as Range[max=>PositiveInt, min=>PositiveInt];
147 ## create subtype via reparameterizing
148 subtype PositiveRangedInt,
149 as RangedInt[PositiveRange];
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.
165 ## re-parameterized subtypes of NameAge containing a Parameterizable Int
166 subtype NameBetween18and35Age,
169 PositiveRangedInt[min=>18,max=>35],
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.
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
184 subtype RequiredAgeInYears,
187 subtype PersonOverAge,
188 as Parameterizable[Person, RequiredAgeInYears]
190 my ($person, $required_years_old) = @_;
191 return $person->years_old > $required_years_old;
194 This would validate the following:
196 my $person = Person->new(age=>35);
197 PersonOverAge([18])->check($person);
199 You can then apply the following coercion
201 coerce PersonOverAge,
203 via {Person->new(%$_)},
205 via {Person->new(age=>$_)};
207 This coercion would then apply to all the following:
209 PersonOverAge([18])->check(30); ## via the Int coercion
210 PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
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
216 coerce PersonOverAge[18],
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.
225 Lastly, the constraining value is available in the coercion in much the
226 same way it is available to the constraint.
228 ## Create a type constraint where a Person must be in the set
230 as Parameterizable[Person, PersonSet],
232 my ($person, $person_set) = @_;
233 $person_set->find($person);
239 my ($hashref, $person_set) = @_;
240 return $person_set->create($hash_ref);
247 This type library defines the following constraints.
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).
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.
260 John Napiorkowski, "<jjnapiork@cpan.org>"
263 This program is free software; you can redistribute it and/or modify it
264 under the same terms as Perl itself.