the basic, basics in place
[gitmo/MooseX-Dependent.git] / lib / MooseX / Dependent / Types.pm
CommitLineData
5964b3ca 1package MooseX::Dependent::Types;
a018b5bb 2
3cfd35fd 3use Moose::Util::TypeConstraints;
a588ee00 4use MooseX::Dependent::Meta::TypeConstraint::Dependent;
5964b3ca 5use MooseX::Types -declare => [qw(Dependent)];
3cfd35fd 6
a018b5bb 7=head1 NAME
8
5964b3ca 9MooseX::Dependent::Types - L<MooseX::Types> constraints that depend on values.
a018b5bb 10
11=head1 SYNOPSIS
12
5964b3ca 13Within your L<MooseX::Types> declared library module:
3cfd35fd 14
613e1e97 15 use MooseX::Dependent::Types qw(Dependent);
16
17 subtype UniqueID,
18 as Dependent[Int, Set],
19 where {
20 my ($int, $set) = @_;
21 return $set->find($int) ? 0:1;
22 };
a018b5bb 23
24=head1 DESCRIPTION
25
5964b3ca 26A L<MooseX::Types> library for creating dependent types. A dependent type
27constraint for all intents and uses is a subclass of a parent type, but adds a
28secondary type parameter which is available to constraint callbacks (such as
29inside the 'where' clause) or in the coercions.
30
31This allows you to create a type that has additional runtime advice, such as a
32set of numbers within which another number must be unique, or allowable ranges
33for a integer, such as in:
34
35 subtype Range,
36 as Dict[max=>Int, min=>Int],
37 where {
38 my ($range) = @_;
39 return $range->{max} > $range->{min};
40 };
41
42 subtype RangedInt,
43 as Dependent[Int, Range],
44 where {
45 my ($value, $range) = @_;
46 return ($value >= $range->{min} &&
47 $value =< $range->{max});
48 };
49
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!
53
54Please note that for ArrayRef or HashRef dependent type constraints, as in the
55example above, as a convenience we automatically ref the incoming type
56parameters, so that the above could also be written as:
57
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!
61
62This is the preferred syntax, as it improve readability and adds to the
63conciseness of your type constraint declarations. An exception wil be thrown if
64your type parameters don't match the required reference type.
65
66==head2 Subtyping a Dependent type constraints
67
68When subclassing a dependent type you must be careful to match either the
69required type parameter type constraint, or if re-parameterizing, the new
70type constraints are a subtype of the parent. For example:
71
72 subtype RangedInt,
73 as Dependent[Int, Range],
74 where {
75 my ($value, $range) = @_;
76 return ($value >= $range->{min} &&
77 $value =< $range->{max});
78 };
79
80Example subtype with additional constraints:
81
82 subtype PositiveRangedInt,
83 as RangedInt,
84 where {
85 shift >= 0;
86 };
87
88Or you could have done the following instead (example of re-paramterizing)
89
90 ## Subtype of Int for positive numbers
91 subtype PositiveInt,
92 as Int,
93 where {
94 shift >= 0;
95 };
96
97 ## subtype Range to re-parameterize Range with subtypes
98 subtype PositveRange,
99 as Range[max=>PositiveInt, min=>PositiveInt];
100
101 ## create subtype via reparameterizing
102 subtype PositiveRangedInt,
103 as RangedInt[PositveRange];
104
105Notice how re-parameterizing the dependent type 'RangedInt' works slightly
106differently from re-parameterizing 'PositiveRange'? Although it initially takes
107two type constraint values to declare a dependent type, should you wish to
108later 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
110type for the dependent type. In other words, given the example above, a type
111constraint of 'RangedInt' would have a parent of 'Int', not 'Dependent' and for
112all intends and uses you could stick it wherever you'd need an Int.
113
114 subtype NameAge,
115 as Tuple[Str, Int];
116
117 ## re-parameterized subtypes of NameAge containing a Dependent Int
118 subtype NameBetween18and35Age,
119 as NameAge[
120 Str,
121 PositiveRangedInt[min=>18,max=>35],
122 ];
123
124One caveat is that you can't stick an unparameterized dependent type inside a
125structure, such as L<MooseX::Types::Structured> since that would require the
126ability to convert a 'containing' type constraint into a dependent type, which
127is a capacity we current don't have.
128
3cfd35fd 129=head2 Coercions
a018b5bb 130
6b2f4f88 131You can place coercions on dependent types, however you need to pay attention to
132what you are actually coercion, the unparameterized or parameterized constraint.
133
5964b3ca 134 TBD: Need discussion and example of coercions working for both the
135 constrainted and dependent type constraint.
136
137 subtype OlderThanAge,
138 as Dependent[Int, Dict[older_than=>Int]],
139 where {
140 my ($value, $dict) = @_;
141 return $value > $dict->{older_than} ? 1:0;
142 };
143
144Which should work like:
145
6b2f4f88 146 OlderThanAge[{older_than=>25}]->check(39); ## is OK
147 OlderThanAge[older_than=>1]->check(9); ## OK, using reference type inference
148
149And you can create coercions like:
150
5964b3ca 151 coerce OlderThanAge,
152 from Tuple[Int, Int],
153 via {
154 my ($int, $int);
155 return [$int, {older_than=>$int}];
156 };
a018b5bb 157
3cfd35fd 158=head2 Recursion
a018b5bb 159
3cfd35fd 160Newer versions of L<MooseX::Types> support recursive type constraints. That is
161you can include a type constraint as a contained type constraint of itself.
162Recursion is support in both the dependent and constraining type constraint. For
5964b3ca 163example, if we assume an Object hierarchy like Food -> [Grass, Meat]
164
165 TODO: DOES THIS EXAMPLE MAKE SENSE?
166
167 subtype Food,
168 as Dependent[Food, Food],
169 where {
170 my ($value, $allowed_food_type) = @_;
171 return $value->isa($allowed_food_type);
172 };
173
174 my $grass = Food::Grass->new;
175 my $meat = Food::Meat->new;
176 my $vegetarian = Food[$grass];
177
178 $vegetarian->check($grass); ## Grass is the allowed food of a vegetarian
179 $vegetarian->check($meat); ## BANG, vegetarian can't eat meat!
a018b5bb 180
3cfd35fd 181=head1 TYPE CONSTRAINTS
a018b5bb 182
3cfd35fd 183This type library defines the following constraints.
a018b5bb 184
5964b3ca 185=head2 Dependent[ParentTypeConstraint, DependentValueTypeConstraint]
a018b5bb 186
5964b3ca 187Create a subtype of ParentTypeConstraint with a dependency on a value that can
188pass the DependentValueTypeConstraint. If DependentValueTypeConstraint is empty
189we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
9b6d2e22 190
5964b3ca 191This creates a type constraint which must be further parameterized at later time
192before it can be used to ->check or ->validate a value. Attempting to do so
193will cause an exception.
a018b5bb 194
195=cut
196
3cfd35fd 197Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
a588ee00 198 MooseX::Dependent::Meta::TypeConstraint::Dependent->new(
5964b3ca 199 name => 'MooseX::Dependent::Types::Dependent',
a588ee00 200 parent => find_type_constraint('Any'),
0a9f5b94 201 constraint => sub {1},
9b6d2e22 202 )
3cfd35fd 203);
9b6d2e22 204
3cfd35fd 205=head1 AUTHOR
a018b5bb 206
3cfd35fd 207John Napiorkowski, C<< <jjnapiork@cpan.org> >>
208
209=head1 COPYRIGHT & LICENSE
a018b5bb 210
211This program is free software; you can redistribute it and/or modify
3cfd35fd 212it under the same terms as Perl itself.
a018b5bb 213
214=cut
9b6d2e22 215
a018b5bb 2161;
a588ee00 217
218__END__
219
220oose::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);
228 },
229 )
230);
231
232
233
234$REGISTRY->add_type_constraint(
235 Moose::Meta::TypeConstraint::Parameterizable->new(
236 name => 'HashRef',
237 package_defined_in => __PACKAGE__,
238 parent => find_type_constraint('Ref'),
239 constraint => sub { ref($_) eq 'HASH' },
240 optimized =>
241 \&Moose::Util::TypeConstraints::OptimizedConstraints::HashRef,
242 constraint_generator => sub {
243 my $type_parameter = shift;
244 my $check = $type_parameter->_compiled_type_constraint;
245 return sub {
246 foreach my $x ( values %$_ ) {
247 ( $check->($x) ) || return;
248 }
249 1;
250 }
251 }
252 )
253);