start renaming to parameterized
[gitmo/MooseX-Dependent.git] / lib / MooseX / Parameterizable / Types.pm
CommitLineData
88f7dcd2 1package MooseX::Parameterizable::Types;
2
3use 5.008;
4
5our $VERSION = '0.01';
6$VERSION = eval $VERSION;
a018b5bb 7
3cfd35fd 8use Moose::Util::TypeConstraints;
88f7dcd2 9use MooseX::Parameterizable::Meta::TypeConstraint::Parameterizable;
10use MooseX::Types -declare => [qw(Parameterizable)];
3cfd35fd 11
a018b5bb 12=head1 NAME
13
88f7dcd2 14MooseX::Parameterizable::Types - Create your own Parameterizable Types.
a018b5bb 15
16=head1 SYNOPSIS
17
5964b3ca 18Within your L<MooseX::Types> declared library module:
3cfd35fd 19
88f7dcd2 20 use MooseX::Parameterizable::Types qw(Parameterizable);
88f58fbf 21
22 subtype Set,
23 as class_type("Set::Scalar");
613e1e97 24
88f58fbf 25 subtype UniqueInt,
88f7dcd2 26 as Parameterizable[Int, Set],
613e1e97 27 where {
28 my ($int, $set) = @_;
88f58fbf 29 return !$set->has($int);
613e1e97 30 };
6c67366e 31
88f58fbf 32 subtype PositiveSet,
33 as Set,
34 where {
35 my ($set) = @_;
36 return !grep {$_ <0 } $set->members;
37 };
38
39 subtype PositiveUniqueInt,
40 as UniqueInt[PositiveSet];
41
42 my $set = Set::Scalar->new(1,2,3);
43
44 UniqueInt([$set])->check(100); ## Okay, 100 isn't in (1,2,3)
45 UniqueInt([$set])->check(-99); ## Okay, -99 isn't in (1,2,3)
46 UniqueInt([$set])->check(2); ## Not OK, 2 is in (1,2,3)
47
48 PositiveUniqueInt([$set])->check(100); ## Okay, 100 isn't in (1,2,3)
49 PositiveUniqueInt([$set])->check(-99); ## Not OK, -99 not Positive Int
50 PositiveUniqueInt([$set])->check(2); ## Not OK, 2 is in (1,2,3)
51
52 my $negative_set = Set::Scalar->new(-1,-2,-3);
53
54 UniqueInt([$negative_set])->check(100); ## Throws exception
55
a018b5bb 56=head1 DESCRIPTION
57
88f7dcd2 58A L<MooseX::Types> library for creating parameterizable types. A parameterizable type
5964b3ca 59constraint for all intents and uses is a subclass of a parent type, but adds a
60secondary type parameter which is available to constraint callbacks (such as
61inside the 'where' clause) or in the coercions.
62
63This allows you to create a type that has additional runtime advice, such as a
64set of numbers within which another number must be unique, or allowable ranges
65for a integer, such as in:
66
67 subtype Range,
68 as Dict[max=>Int, min=>Int],
69 where {
70 my ($range) = @_;
71 return $range->{max} > $range->{min};
72 };
73
74 subtype RangedInt,
88f7dcd2 75 as Parameterizable[Int, Range],
5964b3ca 76 where {
77 my ($value, $range) = @_;
78 return ($value >= $range->{min} &&
6c67366e 79 $value <= $range->{max});
5964b3ca 80 };
81
6c67366e 82 RangedInt([{min=>10,max=>100}])->check(50); ## OK
83 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
84
85This throws a hard Moose exception. You'll need to capture it in an eval or
9c319add 86related exception catching system (see L<TryCatch>).
6c67366e 87
88 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
89
90If you can't accept a hard exception here, you'll need to test the constraining
91values first, as in:
92
93 my $range = {min=>99, max=>10};
94 if(my $err = Range->validate($range)) {
95 ## Handle #$err
96 } else {
97 RangedInt($range)->check(99);
98 }
5964b3ca 99
88f7dcd2 100Please note that for ArrayRef or HashRef parameterizable type constraints, as in the
5964b3ca 101example above, as a convenience we automatically ref the incoming type
102parameters, so that the above could also be written as:
103
6c67366e 104 RangedInt([min=>10,max=>100])->check(50); ## OK
105 RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
106 RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
5964b3ca 107
108This is the preferred syntax, as it improve readability and adds to the
109conciseness of your type constraint declarations. An exception wil be thrown if
110your type parameters don't match the required reference type.
111
6c67366e 112Also not that if you 'chain' parameterization results with a method call like:
113
114 TypeConstraint([$ob])->method;
115
116You need to have the "(...)" around the ArrayRef in the Type Constraint
117parameters. This seems to have something to do with the precendent level of
118"->". Patches or thoughts welcomed. You only need to do this in the above
119case which I imagine is not a very common case.
120
88f7dcd2 121==head2 Subtyping a Parameterizable type constraints
5964b3ca 122
88f7dcd2 123When subclassing a parameterizable type you must be careful to match either the
5964b3ca 124required type parameter type constraint, or if re-parameterizing, the new
125type constraints are a subtype of the parent. For example:
126
127 subtype RangedInt,
88f7dcd2 128 as Parameterizable[Int, Range],
5964b3ca 129 where {
130 my ($value, $range) = @_;
131 return ($value >= $range->{min} &&
132 $value =< $range->{max});
133 };
134
135Example subtype with additional constraints:
136
137 subtype PositiveRangedInt,
138 as RangedInt,
139 where {
140 shift >= 0;
141 };
142
88f58fbf 143Or you could have done the following instead:
5964b3ca 144
145 ## Subtype of Int for positive numbers
146 subtype PositiveInt,
147 as Int,
148 where {
6c67366e 149 my ($value, $range) = @_;
150 return $value >= 0;
5964b3ca 151 };
152
153 ## subtype Range to re-parameterize Range with subtypes
66efbe23 154 subtype PositiveRange,
5964b3ca 155 as Range[max=>PositiveInt, min=>PositiveInt];
156
157 ## create subtype via reparameterizing
158 subtype PositiveRangedInt,
66efbe23 159 as RangedInt[PositiveRange];
5964b3ca 160
88f7dcd2 161Notice how re-parameterizing the parameterizable type 'RangedInt' works slightly
6c67366e 162differently from re-parameterizing 'PositiveRange' Although it initially takes
88f7dcd2 163two type constraint values to declare a parameterizable type, should you wish to
5964b3ca 164later re-parameterize it, you only use a subtype of the second type parameter
88f7dcd2 165(the parameterizable type constraint) since the first type constraint sets the parent
166type for the parameterizable type. In other words, given the example above, a type
167constraint of 'RangedInt' would have a parent of 'Int', not 'Parameterizable' and for
5964b3ca 168all intends and uses you could stick it wherever you'd need an Int.
169
170 subtype NameAge,
171 as Tuple[Str, Int];
172
88f7dcd2 173 ## re-parameterized subtypes of NameAge containing a Parameterizable Int
5964b3ca 174 subtype NameBetween18and35Age,
175 as NameAge[
176 Str,
177 PositiveRangedInt[min=>18,max=>35],
178 ];
179
88f7dcd2 180One caveat is that you can't stick an unparameterized parameterizable type inside a
5964b3ca 181structure, such as L<MooseX::Types::Structured> since that would require the
88f7dcd2 182ability to convert a 'containing' type constraint into a parameterizable type, which
5964b3ca 183is a capacity we current don't have.
184
3cfd35fd 185=head2 Coercions
a018b5bb 186
88f7dcd2 187Parameterizable types have some limited support for coercions. Several things must
26cf337e 188be kept in mind. The first is that the coercion targets the type constraint
88f7dcd2 189which is being made parameterizable, Not the parameterizable type. So for example if you
190create a Parameterizable type like:
26cf337e 191
192 subtype RequiredAgeInYears,
193 as Int;
194
195 subtype PersonOverAge,
88f7dcd2 196 as Parameterizable[Person, RequiredAgeInYears]
26cf337e 197 where {
198 my ($person, $required_years_old) = @_;
199 return $person->years_old > $required_years_old;
200 }
201
202This would validate the following:
203
204 my $person = Person->new(age=>35);
205 PersonOverAge([18])->check($person);
5964b3ca 206
26cf337e 207You can then apply the following coercion
208
209 coerce PersonOverAge,
210 from Dict[age=>int],
211 via {Person->new(%$_)},
212 from Int,
213 via {Person->new(age=>$_)};
214
215This coercion would then apply to all the following:
216
217 PersonOverAge([18])->check(30); ## via the Int coercion
218 PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
219
88f7dcd2 220However, you are not allowed to place coercions on parameterizable types that have
26cf337e 221had their constraining value filled, nor subtypes of such. For example:
222
223 coerce PersonOverAge[18],
224 from DateTime,
225 via {$_->years};
226
227That would generate a hard exception. This is a limitation for now until I can
228devise a smarter way to cache the generated type constraints. However, I doubt
229it will be a significant limitation, since the general use case is supported.
230
231Lastly, the constraining value is available in the coercion in much the same way
232it is available to the constraint.
233
234 ## Create a type constraint where a Person must be in the set
235 subtype PersonInSet,
88f7dcd2 236 as Parameterizable[Person, PersonSet],
5964b3ca 237 where {
26cf337e 238 my ($person, $person_set) = @_;
239 $person_set->find($person);
240 }
6b2f4f88 241
26cf337e 242 coerce PersonInSet,
243 from HashRef,
5964b3ca 244 via {
26cf337e 245 my ($hashref, $person_set) = @_;
246 return $person_set->create($hash_ref);
5964b3ca 247 };
a018b5bb 248
3cfd35fd 249=head2 Recursion
a018b5bb 250
26cf337e 251 TBD
a018b5bb 252
3cfd35fd 253=head1 TYPE CONSTRAINTS
a018b5bb 254
3cfd35fd 255This type library defines the following constraints.
a018b5bb 256
88f7dcd2 257=head2 Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
a018b5bb 258
5964b3ca 259Create a subtype of ParentTypeConstraint with a dependency on a value that can
88f7dcd2 260pass the ParameterizableValueTypeConstraint. If ParameterizableValueTypeConstraint is empty
5964b3ca 261we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
9b6d2e22 262
5964b3ca 263This creates a type constraint which must be further parameterized at later time
264before it can be used to ->check or ->validate a value. Attempting to do so
265will cause an exception.
a018b5bb 266
267=cut
268
3cfd35fd 269Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
88f7dcd2 270 MooseX::Parameterizable::Meta::TypeConstraint::Parameterizable->new(
271 name => 'MooseX::Parameterizable::Types::Parameterizable',
a588ee00 272 parent => find_type_constraint('Any'),
0a9f5b94 273 constraint => sub {1},
9b6d2e22 274 )
3cfd35fd 275);
9b6d2e22 276
3cfd35fd 277=head1 AUTHOR
a018b5bb 278
3cfd35fd 279John Napiorkowski, C<< <jjnapiork@cpan.org> >>
280
281=head1 COPYRIGHT & LICENSE
a018b5bb 282
283This program is free software; you can redistribute it and/or modify
3cfd35fd 284it under the same terms as Perl itself.
a018b5bb 285
286=cut
9b6d2e22 287
a018b5bb 2881;