1 package MooseX::Types::Parameterizable;
6 $VERSION = eval $VERSION;
8 use Moose::Util::TypeConstraints;
9 use MooseX::Meta::TypeConstraint::Parameterizable;
10 use MooseX::Types -declare => [qw(Parameterizable)];
14 MooseX::Types::Parameterizable - Create your own Parameterizable Types.
18 The follow is example usage.
20 package Test::MooseX::Types::Parameterizable::Synopsis;
23 use MooseX::Types::Parameterizable qw(Parameterizable);
24 use MooseX::Types::Moose qw(Str Int ArrayRef);
25 use MooseX::Types -declare=>[qw(Varchar)];
27 Create a type constraint that is similar to SQL Varchar type.
30 as Parameterizable[Str,Int],
32 my($string, $int) = @_;
33 $int >= length($string) ? 1:0;
35 message { "'$_' is too long" };
37 Coerce an ArrayRef to a string via concatenation.
42 my ($arrayref, $int) = @_;
46 has 'varchar_five' => (isa=>Varchar[5], is=>'ro', coerce=>1);
47 has 'varchar_ten' => (isa=>Varchar[10], is=>'ro');
49 Object created since attributes are valid
51 my $object1 = __PACKAGE__->new(
52 varchar_five => '1234',
53 varchar_ten => '123456789',
56 Dies with an invalid constraint for 'varchar_five'
58 my $object2 = __PACKAGE__->new(
59 varchar_five => '12345678', ## too long!
60 varchar_ten => '123456789',
63 varchar_five coerces as expected
65 my $object3 = __PACKAGE__->new(
66 varchar_five => [qw/aa bb/], ## coerces to "aabb"
67 varchar_ten => '123456789',
70 See t/05-pod-examples.t for runnable versions of all POD code
74 A L<MooseX::Types> library for creating parameterizable types. A parameterizable
75 type constraint for all intents and uses is a subclass of a parent type, but
76 adds additional type parameters which are available to constraint callbacks
77 (such as inside the 'where' clause of a type constraint definition) or in the
78 coercions you define for a given type constraint.
80 If you have L<Moose> experience, you probably are familiar with the builtin
81 parameterizable type constraints 'ArrayRef' and 'HashRef'. This type constraint
82 lets you generate your own versions of parameterized constraints that work
83 similarly. See L<Moose::Util::TypeConstraints> for more.
85 Using this type constraint, you can generate new type constraints that have
86 additional runtime advice, such as being able to specify maximum and minimum
87 values for an Int (integer) type constraint:
90 as Dict[max=>Int, min=>Int],
93 return $range->{max} > $range->{min};
97 as Parameterizable[Int, Range],
99 my ($value, $range) = @_;
100 return ($value >= $range->{min} &&
101 $value <= $range->{max});
104 RangedInt([{min=>10,max=>100}])->check(50); ## OK
105 RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, exceeds max
107 This is useful since it lets you generate common patterns of type constraints
108 rather than build a custom type constraint for all similar cases.
110 The type parameter must be valid against the 'constrainting' type constraint used
111 in the Parameterizable condition. If you pass an invalid value this throws a
112 hard Moose exception. You'll need to capture it in an eval or related exception
113 catching system (see L<TryCatch> or L<Try::Tiny>.)
115 For example the following would throw a hard error (and not just return false)
117 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
119 In the above case the 'min' value is larger than the 'max', which violates the
120 Range constraint. We throw a hard error here since I think incorrect type
121 parameters are most likely to be the result of a typo or other true error
124 If you can't accept a hard exception here, you can either trap it as advised
125 above or you need to test the constraining values first, as in:
127 my $range = {min=>99, max=>10};
128 if(my $err = Range->validate($range)) {
131 RangedInt($range)->check(99);
134 Please note that for ArrayRef or HashRef parameterizable type constraints, as in the
135 example above, as a convenience we automatically ref the incoming type
136 parameters, so that the above could also be written as:
138 RangedInt([min=>10,max=>100])->check(50); ## OK
139 RangedInt([min=>50, max=>75])->check(99); ## Not OK, exceeds max
140 RangedInt([min=>99, max=>10])->check(10); ## Exception, not valid Range
142 This is the preferred syntax, as it improve readability and adds to the
143 conciseness of your type constraint declarations.
145 Also note that if you 'chain' parameterization results with a method call like:
147 TypeConstraint([$ob])->method;
149 You need to have the "(...)" around the ArrayRef in the Type Constraint
150 parameters. You can skip the wrapping parenthesis in the most common cases,
151 such as when you use the type constraint in the options section of a L<Moose>
152 attribute declaration, or when defining type libraries.
154 =head2 Subtyping a Parameterizable type constraints
156 When subclassing a parameterizable type you must be careful to match either the
157 required type parameter type constraint, or if re-parameterizing, the new
158 type constraints are a subtype of the parent. For example:
161 as Parameterizable[Int, Range],
163 my ($value, $range) = @_;
164 return ($value >= $range->{min} &&
165 $value =< $range->{max});
168 Example subtype with additional constraints:
170 subtype PositiveRangedInt,
176 In this case you'd now have a parameterizable type constraint which would
179 Test::More::ok PositiveRangedInt([{min=>-10, max=>75}])->check(5);
180 Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5);
182 Of course the above is somewhat counter-intuitive to the reader, since we have
183 defined our 'RangedInt' in such as way as to let you declare negative ranges.
184 For the moment each type constraint rule is apply without knowledge of any
185 other rule, nor can a rule 'inform' existing rules. This is a limitation of
186 the current system. However, you could instead do the following:
189 ## Subtype of Int for positive numbers
193 my ($value, $range) = @_;
197 ## subtype Range to re-parameterize Range with subtypes
198 subtype PositiveRange,
199 as Range[max=>PositiveInt, min=>PositiveInt];
201 ## create subtype via reparameterizing
202 subtype PositiveRangedInt,
203 as RangedInt[PositiveRange];
205 This would constrain values in the same way as the previous type constraint but
206 have the bonus that you'd throw a hard exception if you try to use an incorrect
209 Test::More::ok PositiveRangedInt([{min=>10, max=>75}])->check(15); ## OK
210 Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5); ## Dies
212 Notice how re-parameterizing the parameterizable type 'RangedInt' works slightly
213 differently from re-parameterizing 'PositiveRange' Although it initially takes
214 two type constraint values to declare a parameterizable type, should you wish to
215 later re-parameterize it, you only use a subtype of the extra type parameter
216 (the parameterizable type constraints) since the first type constraint sets the
217 parent type for the parameterizable type.
219 In other words, given the example above, a type constraint of 'RangedInt' would
220 have a parent of 'Int', not 'Parameterizable' and for all intends and uses you
221 could stick it wherever you'd need an Int. You can't change the parent, even
222 to make it a subclass of Int.
226 A type coercion is a rule that allows you to transform one type from one or
227 more other types. Please see L<Moose::Cookbook::Basics::Recipe5> for an example
228 of type coercions if you are not familiar with the subject.
230 L<MooseX::Types::Parameterizable> supports type coercions in all the ways you
231 would expect. In addition, it also supports a limited form of type coercion
232 inheritance. Generally speaking, type constraints don't inherit coercions since
233 this would rapidly become confusing. However, since your parameterizable type
234 is intended to become parameterized in order to be useful, we support inheriting
235 from a 'base' parameterizable type constraint to its 'child' parameterized sub
238 For the purposes of this discussion, a parameterizable type is a subtype created
239 when you say, "as Parameterizable[..." in your sub type declaration. For
243 as Parameterizable[Str, Int],
245 my($string, $int) = @_;
246 $int >= length($string) ? 1:0;
248 message { "'$_' is too long" };
250 This is the L</SYNOPSIS> example, which creates a new parameterizable subtype of
251 Str which takes a single type parameter which must be an Int. This Int is used
252 to constrain the allowed length of the Str value.
254 Now, this new sub type, "Varchar", is parameterizable since it can take a type
255 parameter. We can apply some coercions to it:
259 via { "$_"; }, ## stringify the object
261 via { join '',@$_ }; ## convert array to string
263 This parameterizable subtype, "Varchar" itself is something you'd never use
264 directly to constraint a value. In other words you'd never do something like:
266 has name => (isa=>Varchar, ...); ## Why not just use a Str?
268 You are going to do this:
270 has name => (isa=>Varchar[40], ...)
272 Which is actually useful. However, "Varchar[40]" is a parameterized type, it
273 is a subtype of the parameterizable "Varchar" and it inherits coercions from
274 its parent. This may be a bit surprising to L<Moose> developers, but I believe
275 this is the actual desired behavior.
277 You can of course add new coercions to a subtype of a parameterizable type:
279 subtype MySpecialVarchar,
282 coerce MySpecialVarchar,
285 In which case this new parameterizable type would NOT inherit coercions from
286 it's parent parameterizable type (Varchar). This is done in keeping with how
287 generally speaking L<Moose> type constraints avoid complicated coercion inheritance
288 schemes, however I am open to discussion if there are valid use cases.
290 NOTE: One thing you can't do is add a coercion to an already parameterized type.
291 Currently the following would throw a hard error:
296 coerce 40CharStr, ... # BANG!
298 This limitation is enforced since generally we expect coercions on the parent.
299 However if good use cases arise we may lift this in the future.
301 In general we are trying to take a conservative approach that keeps in line with
302 how most L<Moose> authors expect type constraints to work.
306 TBD - Needs a use case... Anyone?
308 =head1 TYPE CONSTRAINTS
310 This type library defines the following constraints.
312 =head2 Parameterizable[ParentTypeConstraint, ConstrainingValueTypeConstraint]
314 Create a subtype of ParentTypeConstraint with a dependency on a value that can
315 pass the ConstrainingValueTypeConstraint. If ConstrainingValueTypeConstraint is empty
316 we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
317 This is useful if you are creating some base Parameterizable type constraints
318 that you intend to sub class.
322 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
323 MooseX::Meta::TypeConstraint::Parameterizable->new(
324 name => 'MooseX::Types::Parameterizable::Parameterizable',
325 parent => find_type_constraint('Any'),
326 constraint => sub {1},
332 The following modules or resources may be of interest.
334 L<Moose>, L<Moose::Meta::TypeConstraint>, L<MooseX::Types>
338 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
340 =head1 COPYRIGHT & LICENSE
342 This program is free software; you can redistribute it and/or modify
343 it under the same terms as Perl itself.