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