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
52 RangedInt[{min=>99, max=>10}]->check(10); ## Not OK, not a valid Range!
54 Please note that for ArrayRef or HashRef dependent type constraints, as in the
55 example above, as a convenience we automatically ref the incoming type
56 parameters, so that the above could also be written as:
58 RangedInt[min=>10,max=>100]->check(50); ## OK
59 RangedInt[min=>50, max=>75]->check(99); ## Not OK, 99 exceeds max
60 RangedInt[min=>99, max=>10]->check(10); ## Not OK, not a valid Range!
62 This is the preferred syntax, as it improve readability and adds to the
63 conciseness of your type constraint declarations. An exception wil be thrown if
64 your type parameters don't match the required reference type.
66 ==head2 Subtyping a Dependent type constraints
68 When subclassing a dependent type you must be careful to match either the
69 required type parameter type constraint, or if re-parameterizing, the new
70 type constraints are a subtype of the parent. For example:
73 as Dependent[Int, Range],
75 my ($value, $range) = @_;
76 return ($value >= $range->{min} &&
77 $value =< $range->{max});
80 Example subtype with additional constraints:
82 subtype PositiveRangedInt,
88 Or you could have done the following instead (example of re-paramterizing)
90 ## Subtype of Int for positive numbers
97 ## subtype Range to re-parameterize Range with subtypes
98 subtype PositiveRange,
99 as Range[max=>PositiveInt, min=>PositiveInt];
101 ## create subtype via reparameterizing
102 subtype PositiveRangedInt,
103 as RangedInt[PositiveRange];
105 Notice how re-parameterizing the dependent type 'RangedInt' works slightly
106 differently from re-parameterizing 'PositiveRange'? Although it initially takes
107 two type constraint values to declare a dependent type, should you wish to
108 later re-parameterize it, you only use a subtype of the second type parameter
109 (the dependent type constraint) since the first type constraint sets the parent
110 type for the dependent type. In other words, given the example above, a type
111 constraint of 'RangedInt' would have a parent of 'Int', not 'Dependent' and for
112 all intends and uses you could stick it wherever you'd need an Int.
117 ## re-parameterized subtypes of NameAge containing a Dependent Int
118 subtype NameBetween18and35Age,
121 PositiveRangedInt[min=>18,max=>35],
124 One caveat is that you can't stick an unparameterized dependent type inside a
125 structure, such as L<MooseX::Types::Structured> since that would require the
126 ability to convert a 'containing' type constraint into a dependent type, which
127 is a capacity we current don't have.
131 You can place coercions on dependent types, however you need to pay attention to
132 what you are actually coercion, the unparameterized or parameterized constraint.
134 TBD: Need discussion and example of coercions working for both the
135 constrainted and dependent type constraint.
137 subtype OlderThanAge,
138 as Dependent[Int, Dict[older_than=>Int]],
140 my ($value, $dict) = @_;
141 return $value > $dict->{older_than} ? 1:0;
144 Which should work like:
146 OlderThanAge[{older_than=>25}]->check(39); ## is OK
147 OlderThanAge[older_than=>1]->check(9); ## OK, using reference type inference
149 And you can create coercions like:
152 from Tuple[Int, Int],
155 return [$int, {older_than=>$int}];
160 Newer versions of L<MooseX::Types> support recursive type constraints. That is
161 you can include a type constraint as a contained type constraint of itself.
162 Recursion is support in both the dependent and constraining type constraint. For
163 example, if we assume an Object hierarchy like Food -> [Grass, Meat]
165 TODO: DOES THIS EXAMPLE MAKE SENSE?
168 as Dependent[Food, Food],
170 my ($value, $allowed_food_type) = @_;
171 return $value->isa($allowed_food_type);
174 my $grass = Food::Grass->new;
175 my $meat = Food::Meat->new;
176 my $vegetarian = Food[$grass];
178 $vegetarian->check($grass); ## Grass is the allowed food of a vegetarian
179 $vegetarian->check($meat); ## BANG, vegetarian can't eat meat!
181 =head1 TYPE CONSTRAINTS
183 This type library defines the following constraints.
185 =head2 Dependent[ParentTypeConstraint, DependentValueTypeConstraint]
187 Create a subtype of ParentTypeConstraint with a dependency on a value that can
188 pass the DependentValueTypeConstraint. If DependentValueTypeConstraint is empty
189 we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
191 This creates a type constraint which must be further parameterized at later time
192 before it can be used to ->check or ->validate a value. Attempting to do so
193 will cause an exception.
197 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
198 MooseX::Dependent::Meta::TypeConstraint::Dependent->new(
199 name => 'MooseX::Dependent::Types::Dependent',
200 parent => find_type_constraint('Any'),
201 constraint => sub {1},
207 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
209 =head1 COPYRIGHT & LICENSE
211 This program is free software; you can redistribute it and/or modify
212 it under the same terms as Perl itself.
220 oose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
221 Moose::Meta::TypeConstraint::Parameterizable->new(
222 name => 'MooseX::Dependent::Types::Dependent',
223 parent => find_type_constraint('Any'),
224 constraint => sub { 0 },
225 constraint_generator=> sub {
226 my ($dependent_val, $callback, $constraining_val) = @_;
227 return $callback->($dependent_val, $constraining_val);
234 $REGISTRY->add_type_constraint(
235 Moose::Meta::TypeConstraint::Parameterizable->new(
237 package_defined_in => __PACKAGE__,
238 parent => find_type_constraint('Ref'),
239 constraint => sub { ref($_) eq 'HASH' },
241 \&Moose::Util::TypeConstraints::OptimizedConstraints::HashRef,
242 constraint_generator => sub {
243 my $type_parameter = shift;
244 my $check = $type_parameter->_compiled_type_constraint;
246 foreach my $x ( values %$_ ) {
247 ( $check->($x) ) || return;