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
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 The type parameter must be valid against the type constraint given. If you pass
108 an invalid value this throws a hard Moose exception. You'll need to capture it
109 in an eval or related exception catching system (see L<TryCatch> or <Try::Tiny>.)
110 For example the following would throw a hard error (and not just return false)
112 RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
114 If you can't accept a hard exception here, you'll need to test the constraining
117 my $range = {min=>99, max=>10};
118 if(my $err = Range->validate($range)) {
121 RangedInt($range)->check(99);
124 Please note that for ArrayRef or HashRef parameterizable type constraints, as in the
125 example above, as a convenience we automatically ref the incoming type
126 parameters, so that the above could also be written as:
128 RangedInt([min=>10,max=>100])->check(50); ## OK
129 RangedInt([min=>50, max=>75])->check(99); ## Not OK, exceeds max
130 RangedInt([min=>99, max=>10])->check(10); ## Exception, not valid Range
132 This is the preferred syntax, as it improve readability and adds to the
133 conciseness of your type constraint declarations. An exception wil be thrown if
134 your type parameters don't match the required reference type.
136 Also note that if you 'chain' parameterization results with a method call like:
138 TypeConstraint([$ob])->method;
140 You need to have the "(...)" around the ArrayRef in the Type Constraint
141 parameters. You can skip the wrapping parenthesis in the most common cases,
142 such as when you use the type constraint in the options section of a L<Moose>
143 attribute declaration, or when defining type libraries.
145 =head2 Subtyping a Parameterizable type constraints
147 When subclassing a parameterizable type you must be careful to match either the
148 required type parameter type constraint, or if re-parameterizing, the new
149 type constraints are a subtype of the parent. For example:
152 as Parameterizable[Int, Range],
154 my ($value, $range) = @_;
155 return ($value >= $range->{min} &&
156 $value =< $range->{max});
159 Example subtype with additional constraints:
161 subtype PositiveRangedInt,
167 In this case you'd now have a parameterizable type constraint called which
170 Test::More::ok PositiveRangedInt([{min=>-10, max=>75}])->check(5);
171 Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5);
173 Of course the above is somewhat counter-intuitive to the reader, since we have
174 defined our 'RangedInt' in such as way as to let you declare negative ranges.
175 For the moment each type constraint rule is apply without knowledge of any
176 other rule, nor can a rule 'inform' existing rules. This is a limitation of
177 the current system. However, you could instead do the following:
180 ## Subtype of Int for positive numbers
184 my ($value, $range) = @_;
188 ## subtype Range to re-parameterize Range with subtypes
189 subtype PositiveRange,
190 as Range[max=>PositiveInt, min=>PositiveInt];
192 ## create subtype via reparameterizing
193 subtype PositiveRangedInt,
194 as RangedInt[PositiveRange];
196 This would constrain values in the same way as the previous type constraint but
197 have the bonus that you'd throw a hard exception if you try to use an incorrect
200 Test::More::ok PositiveRangedInt([{min=>10, max=>75}])->check(15); ## OK
201 Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5); ## Dies
203 Notice how re-parameterizing the parameterizable type 'RangedInt' works slightly
204 differently from re-parameterizing 'PositiveRange' Although it initially takes
205 two type constraint values to declare a parameterizable type, should you wish to
206 later re-parameterize it, you only use a subtype of the extra type parameter
207 (the parameterizable type constraints) since the first type constraint sets the
208 parent type for the parameterizable type.
210 In other words, given the example above, a type constraint of 'RangedInt' would
211 have a parent of 'Int', not 'Parameterizable' and for all intends and uses you
212 could stick it wherever you'd need an Int.
216 A type coerction is a rule that allows you to transform one type from one or
217 more other types. Please see L<Moose::Cookbook::Basics::Recipe5> for an example
218 of type coercions if you are not familiar with the subject.
220 L<MooseX::Types::Parameterizable> support type coercions in all the ways you
221 would expect. In addition, it also supports a limited form of type coercion
222 inheritance. Generally speaking, type constraints don't inherit coercions since
223 this would rapidly become confusing. However, since your parameterizable type
224 is intended to become parameterized in order to be useful, we support inheriting
225 from a 'base' parameterizable type constraint to its 'child' parameterized sub
228 For the purposes of this discussion, a parameterizable type is a subtype created
229 when you say, "as Parameterizable[..." in your sub type declaration. For example
232 as Parameterizable[Str, Int],
234 my($string, $int) = @_;
235 $int >= length($string) ? 1:0;
237 message { "'$_' is too long" };
239 This is the </SYNOPSIS> example, which creates a new parameterizable subtype of
240 Str which takes a single type parameter which must be an Int. This Int is used
241 to constrain the allowed length of the Str value.
243 Now, this new sub type, "Varchar", is parameterizable since it can take a type
244 parameter. We can apply some coercions to it:
248 via { "$_"; }, ## stringify the object
250 via { join '',@$_ }; ## convert array to string
252 This parameterizable subtype, "Varchar" itself is something you'd never use
253 directly to constraint a value. In other words you'd never do something like:
255 has name => (isa=>Varchar, ...)
257 You are going to do this:
259 has name => (isa=>Varchar[40], ...)
261 Which is actually useful. However, "Varchar[40]" is a parameterized type, it
262 is a subtype of the parameterizable "Varchar" and it inherits coercions from
263 its parent. This may be a bit surprising to L<Moose> developers, but I believe
264 this is the actual desired behavior.
266 You can of course add new coercions to a subtype of a parameterizable type:
268 subtype MySpecialVarchar,
271 coerce MySpecialVarchar,
274 In which case this new parameterizable type would NOT inherit coercions from
275 it's parent parameterizable type (Varchar). This is done in keeping with how
276 generally speaking L<Moose> type constraints avoid complicated coercion inheritance
277 schemes, however I am open to discussion if there are valid use cases.
279 NOTE: One thing you can't do is add a coercion to an already parameterized type.
280 Currently the following would throw a hard error:
285 coerce 40CharStr, ... # BANG!
287 This limitation is enforced since generally we expect coercions on the parent.
288 However if good use cases arise we may lift this in the future.
290 In general we are trying to take a conservative approach that keeps in line with
291 how most L<Moose> authors expect type constraints to work.
295 TBD - Needs a use case... Anyone?
297 =head1 TYPE CONSTRAINTS
299 This type library defines the following constraints.
301 =head2 Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
303 Create a subtype of ParentTypeConstraint with a dependency on a value that can
304 pass the ParameterizableValueTypeConstraint. If ParameterizableValueTypeConstraint is empty
305 we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
307 This creates a type constraint which must be further parameterized at later time
308 before it can be used to ->check or ->validate a value. Attempting to do so
309 will cause an exception.
313 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
314 MooseX::Meta::TypeConstraint::Parameterizable->new(
315 name => 'MooseX::Types::Parameterizable::Parameterizable',
316 parent => find_type_constraint('Any'),
317 constraint => sub {1},
323 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
325 =head1 COPYRIGHT & LICENSE
327 This program is free software; you can redistribute it and/or modify
328 it under the same terms as Perl itself.