1 package MooseX::Dependent::Types;
3 use Moose::Util::TypeConstraints;
4 use MooseX::Dependent::Meta::TypeConstraint::Dependent;
5 use MooseX::Types -declare => [qw(Dependent)];
9 MooseX::Dependent::Types - L<MooseX::Types> constraints that depend on values.
13 Within your L<MooseX::Types> declared library module:
15 use MooseX::Dependent::Types qw(Dependent);
18 as Dependent[Int, Set],
21 return $set->find($int) ? 0:1;
26 A L<MooseX::Types> library for creating dependent types. A dependent type
27 constraint for all intents and uses is a subclass of a parent type, but adds a
28 secondary type parameter which is available to constraint callbacks (such as
29 inside the 'where' clause) or in the coercions.
31 This allows you to create a type that has additional runtime advice, such as a
32 set of numbers within which another number must be unique, or allowable ranges
33 for a integer, such as in:
36 as Dict[max=>Int, min=>Int],
39 return $range->{max} > $range->{min};
43 as Dependent[Int, Range],
45 my ($value, $range) = @_;
46 return ($value >= $range->{min} &&
47 $value <= $range->{max});
50 RangedInt([{min=>10,max=>100}])->check(50); ## OK
51 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
53 This throws a hard Moose exception. You'll need to capture it in an eval or
54 related exception catching system (see L<TryCatch>).
56 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
58 If you can't accept a hard exception here, you'll need to test the constraining
61 my $range = {min=>99, max=>10};
62 if(my $err = Range->validate($range)) {
65 RangedInt($range)->check(99);
68 Please note that for ArrayRef or HashRef dependent type constraints, as in the
69 example above, as a convenience we automatically ref the incoming type
70 parameters, so that the above could also be written as:
72 RangedInt([min=>10,max=>100])->check(50); ## OK
73 RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
74 RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
76 This is the preferred syntax, as it improve readability and adds to the
77 conciseness of your type constraint declarations. An exception wil be thrown if
78 your type parameters don't match the required reference type.
80 Also not that if you 'chain' parameterization results with a method call like:
82 TypeConstraint([$ob])->method;
84 You need to have the "(...)" around the ArrayRef in the Type Constraint
85 parameters. This seems to have something to do with the precendent level of
86 "->". Patches or thoughts welcomed. You only need to do this in the above
87 case which I imagine is not a very common case.
89 ==head2 Subtyping a Dependent type constraints
91 When subclassing a dependent type you must be careful to match either the
92 required type parameter type constraint, or if re-parameterizing, the new
93 type constraints are a subtype of the parent. For example:
96 as Dependent[Int, Range],
98 my ($value, $range) = @_;
99 return ($value >= $range->{min} &&
100 $value =< $range->{max});
103 Example subtype with additional constraints:
105 subtype PositiveRangedInt,
111 Or you could have done the following instead (example of re-paramterizing)
113 ## Subtype of Int for positive numbers
117 my ($value, $range) = @_;
121 ## subtype Range to re-parameterize Range with subtypes
122 subtype PositiveRange,
123 as Range[max=>PositiveInt, min=>PositiveInt];
125 ## create subtype via reparameterizing
126 subtype PositiveRangedInt,
127 as RangedInt[PositiveRange];
129 Notice how re-parameterizing the dependent type 'RangedInt' works slightly
130 differently from re-parameterizing 'PositiveRange' Although it initially takes
131 two type constraint values to declare a dependent type, should you wish to
132 later re-parameterize it, you only use a subtype of the second type parameter
133 (the dependent type constraint) since the first type constraint sets the parent
134 type for the dependent type. In other words, given the example above, a type
135 constraint of 'RangedInt' would have a parent of 'Int', not 'Dependent' and for
136 all intends and uses you could stick it wherever you'd need an Int.
141 ## re-parameterized subtypes of NameAge containing a Dependent Int
142 subtype NameBetween18and35Age,
145 PositiveRangedInt[min=>18,max=>35],
148 One caveat is that you can't stick an unparameterized dependent type inside a
149 structure, such as L<MooseX::Types::Structured> since that would require the
150 ability to convert a 'containing' type constraint into a dependent type, which
151 is a capacity we current don't have.
155 Dependent types have some limited support for coercions. Several things must
156 be kept in mind. The first is that the coercion targets the type constraint
157 which is being made dependent, Not the dependent type. So for example if you
158 create a Dependent type like:
160 subtype RequiredAgeInYears,
163 subtype PersonOverAge,
164 as Dependent[Person, RequiredAgeInYears]
166 my ($person, $required_years_old) = @_;
167 return $person->years_old > $required_years_old;
170 This would validate the following:
172 my $person = Person->new(age=>35);
173 PersonOverAge([18])->check($person);
175 You can then apply the following coercion
177 coerce PersonOverAge,
179 via {Person->new(%$_)},
181 via {Person->new(age=>$_)};
183 This coercion would then apply to all the following:
185 PersonOverAge([18])->check(30); ## via the Int coercion
186 PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
188 However, you are not allowed to place coercions on dependent types that have
189 had their constraining value filled, nor subtypes of such. For example:
191 coerce PersonOverAge[18],
195 That would generate a hard exception. This is a limitation for now until I can
196 devise a smarter way to cache the generated type constraints. However, I doubt
197 it will be a significant limitation, since the general use case is supported.
199 Lastly, the constraining value is available in the coercion in much the same way
200 it is available to the constraint.
202 ## Create a type constraint where a Person must be in the set
204 as Dependent[Person, PersonSet],
206 my ($person, $person_set) = @_;
207 $person_set->find($person);
213 my ($hashref, $person_set) = @_;
214 return $person_set->create($hash_ref);
221 =head1 TYPE CONSTRAINTS
223 This type library defines the following constraints.
225 =head2 Dependent[ParentTypeConstraint, DependentValueTypeConstraint]
227 Create a subtype of ParentTypeConstraint with a dependency on a value that can
228 pass the DependentValueTypeConstraint. If DependentValueTypeConstraint is empty
229 we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
231 This creates a type constraint which must be further parameterized at later time
232 before it can be used to ->check or ->validate a value. Attempting to do so
233 will cause an exception.
237 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
238 MooseX::Dependent::Meta::TypeConstraint::Dependent->new(
239 name => 'MooseX::Dependent::Types::Dependent',
240 parent => find_type_constraint('Any'),
241 constraint => sub {1},
247 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
249 =head1 COPYRIGHT & LICENSE
251 This program is free software; you can redistribute it and/or modify
252 it under the same terms as Perl itself.