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<Try::Catch>).
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 You can place coercions on dependent types, however you need to pay attention to
156 what you are actually coercion, the unparameterized or parameterized constraint.
158 TBD: Need discussion and example of coercions working for both the
159 constrainted and dependent type constraint.
161 subtype OlderThanAge,
162 as Dependent[Int, Dict[older_than=>Int]],
164 my ($value, $dict) = @_;
165 return $value > $dict->{older_than} ? 1:0;
168 Which should work like:
170 OlderThanAge([{older_than=>25}])->check(39); ## is OK
171 OlderThanAge([older_than=>1])->check(9); ## OK, using reference type inference
173 And you can create coercions like:
176 from Tuple[Int, Int],
179 return [$int, {older_than=>$int}];
184 Newer versions of L<MooseX::Types> support recursive type constraints. That is
185 you can include a type constraint as a contained type constraint of itself.
186 Recursion is support in both the dependent and constraining type constraint. For
187 example, if we assume an Object hierarchy like Food -> [Grass, Meat]
189 TODO: DOES THIS EXAMPLE MAKE SENSE?
192 as Dependent[Food, Food],
194 my ($value, $allowed_food_type) = @_;
195 return $value->isa($allowed_food_type);
198 my $grass = Food::Grass->new;
199 my $meat = Food::Meat->new;
200 my $vegetarian = Food[$grass];
202 $vegetarian->check($grass); ## Grass is the allowed food of a vegetarian
203 $vegetarian->check($meat); ## BANG, vegetarian can't eat meat!
205 =head1 TYPE CONSTRAINTS
207 This type library defines the following constraints.
209 =head2 Dependent[ParentTypeConstraint, DependentValueTypeConstraint]
211 Create a subtype of ParentTypeConstraint with a dependency on a value that can
212 pass the DependentValueTypeConstraint. If DependentValueTypeConstraint is empty
213 we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
215 This creates a type constraint which must be further parameterized at later time
216 before it can be used to ->check or ->validate a value. Attempting to do so
217 will cause an exception.
221 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
222 MooseX::Dependent::Meta::TypeConstraint::Dependent->new(
223 name => 'MooseX::Dependent::Types::Dependent',
224 parent => find_type_constraint('Any'),
225 constraint => sub {1},
231 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
233 =head1 COPYRIGHT & LICENSE
235 This program is free software; you can redistribute it and/or modify
236 it under the same terms as Perl itself.