Within your MooseX::Types declared library module:
use MooseX::Types::Parameterizable qw(Parameterizable);
-
- subtype Set,
- as class_type("Set::Scalar");
+
+ subtype Set,
+ as class_type("Set::Scalar");
subtype UniqueInt,
as Parameterizable[Int, Set],
my ($int, $set) = @_;
return !$set->has($int);
};
-
- subtype PositiveSet,
- as Set,
- where {
- my ($set) = @_;
- return !grep {$_ <0 } $set->members;
- };
-
- subtype PositiveUniqueInt,
- as UniqueInt[PositiveSet];
-
- my $set = Set::Scalar->new(1,2,3);
-
- UniqueInt([$set])->check(100); ## Okay, 100 isn't in (1,2,3)
- UniqueInt([$set])->check(-99); ## Okay, -99 isn't in (1,2,3)
- UniqueInt([$set])->check(2); ## Not OK, 2 is in (1,2,3)
-
- PositiveUniqueInt([$set])->check(100); ## Okay, 100 isn't in (1,2,3)
- PositiveUniqueInt([$set])->check(-99); ## Not OK, -99 not Positive Int
- PositiveUniqueInt([$set])->check(2); ## Not OK, 2 is in (1,2,3)
- my $negative_set = Set::Scalar->new(-1,-2,-3);
+ subtype PositiveSet,
+ as Set,
+ where {
+ my ($set) = @_;
+ return !grep {$_ <0 } $set->members;
+ };
- UniqueInt([$negative_set])->check(100); ## Throws exception
+ subtype PositiveUniqueInt,
+ as UniqueInt[PositiveSet];
+
+ my $set = Set::Scalar->new(1,2,3);
+
+ UniqueInt([$set])->check(100); ## Okay, 100 isn't in (1,2,3)
+ UniqueInt([$set])->check(-99); ## Okay, -99 isn't in (1,2,3)
+ UniqueInt([$set])->check(2); ## Not OK, 2 is in (1,2,3)
+
+ PositiveUniqueInt([$set])->check(100); ## Okay, 100 isn't in (1,2,3)
+ PositiveUniqueInt([$set])->check(-99); ## Not OK, -99 not Positive Int
+ PositiveUniqueInt([$set])->check(2); ## Not OK, 2 is in (1,2,3)
+
+ my $negative_set = Set::Scalar->new(-1,-2,-3);
+
+ UniqueInt([$negative_set])->check(100); ## Throws exception
DESCRIPTION
A MooseX::Types library for creating parameterizable types. A
such as a set of numbers within which another number must be unique, or
allowable ranges for a integer, such as in:
- subtype Range,
- as Dict[max=>Int, min=>Int],
- where {
- my ($range) = @_;
- return $range->{max} > $range->{min};
- };
-
- subtype RangedInt,
- as Parameterizable[Int, Range],
- where {
- my ($value, $range) = @_;
- return ($value >= $range->{min} &&
- $value <= $range->{max});
- };
-
- RangedInt([{min=>10,max=>100}])->check(50); ## OK
- RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
+ subtype Range,
+ as Dict[max=>Int, min=>Int],
+ where {
+ my ($range) = @_;
+ return $range->{max} > $range->{min};
+ };
+
+ subtype RangedInt,
+ as Parameterizable[Int, Range],
+ where {
+ my ($value, $range) = @_;
+ return ($value >= $range->{min} &&
+ $value <= $range->{max});
+ };
+
+ RangedInt([{min=>10,max=>100}])->check(50); ## OK
+ RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
This throws a hard Moose exception. You'll need to capture it in an eval
- or related exception catching system (see TryCatch).
+ or related exception catching system (see TryCatch or <Try::Tiny>.)
- RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
+ RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
If you can't accept a hard exception here, you'll need to test the
constraining values first, as in:
- my $range = {min=>99, max=>10};
- if(my $err = Range->validate($range)) {
- ## Handle #$err
- } else {
- RangedInt($range)->check(99);
- }
+ my $range = {min=>99, max=>10};
+ if(my $err = Range->validate($range)) {
+ ## Handle #$err
+ } else {
+ RangedInt($range)->check(99);
+ }
Please note that for ArrayRef or HashRef parameterizable type
constraints, as in the example above, as a convenience we automatically
ref the incoming type parameters, so that the above could also be
written as:
- RangedInt([min=>10,max=>100])->check(50); ## OK
- RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
- RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
+ RangedInt([min=>10,max=>100])->check(50); ## OK
+ RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
+ RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
This is the preferred syntax, as it improve readability and adds to the
conciseness of your type constraint declarations. An exception wil be
Also not that if you 'chain' parameterization results with a method call
like:
- TypeConstraint([$ob])->method;
+ TypeConstraint([$ob])->method;
You need to have the "(...)" around the ArrayRef in the Type Constraint
parameters. This seems to have something to do with the precendent level
re-parameterizing, the new type constraints are a subtype of the parent.
For example:
- subtype RangedInt,
- as Parameterizable[Int, Range],
- where {
- my ($value, $range) = @_;
- return ($value >= $range->{min} &&
- $value =< $range->{max});
- };
+ subtype RangedInt,
+ as Parameterizable[Int, Range],
+ where {
+ my ($value, $range) = @_;
+ return ($value >= $range->{min} &&
+ $value =< $range->{max});
+ };
Example subtype with additional constraints:
- subtype PositiveRangedInt,
- as RangedInt,
- where {
- shift >= 0;
- };
+ subtype PositiveRangedInt,
+ as RangedInt,
+ where {
+ shift >= 0;
+ };
Or you could have done the following instead:
- ## Subtype of Int for positive numbers
- subtype PositiveInt,
- as Int,
- where {
- my ($value, $range) = @_;
- return $value >= 0;
- };
-
- ## subtype Range to re-parameterize Range with subtypes
- subtype PositiveRange,
- as Range[max=>PositiveInt, min=>PositiveInt];
-
- ## create subtype via reparameterizing
- subtype PositiveRangedInt,
- as RangedInt[PositiveRange];
+ ## Subtype of Int for positive numbers
+ subtype PositiveInt,
+ as Int,
+ where {
+ my ($value, $range) = @_;
+ return $value >= 0;
+ };
+
+ ## subtype Range to re-parameterize Range with subtypes
+ subtype PositiveRange,
+ as Range[max=>PositiveInt, min=>PositiveInt];
+
+ ## create subtype via reparameterizing
+ subtype PositiveRangedInt,
+ as RangedInt[PositiveRange];
Notice how re-parameterizing the parameterizable type 'RangedInt' works
slightly differently from re-parameterizing 'PositiveRange' Although it
'Parameterizable' and for all intends and uses you could stick it
wherever you'd need an Int.
- subtype NameAge,
- as Tuple[Str, Int];
-
- ## re-parameterized subtypes of NameAge containing a Parameterizable Int
- subtype NameBetween18and35Age,
- as NameAge[
- Str,
- PositiveRangedInt[min=>18,max=>35],
- ];
+ subtype NameAge,
+ as Tuple[Str, Int];
+
+ ## re-parameterized subtypes of NameAge containing a Parameterizable Int
+ subtype NameBetween18and35Age,
+ as NameAge[
+ Str,
+ PositiveRangedInt[min=>18,max=>35],
+ ];
One caveat is that you can't stick an unparameterized parameterizable
type inside a structure, such as MooseX::Types::Structured since that
parameterizable type. So for example if you create a Parameterizable
type like:
- subtype RequiredAgeInYears,
- as Int;
+ subtype RequiredAgeInYears,
+ as Int;
- subtype PersonOverAge,
- as Parameterizable[Person, RequiredAgeInYears]
- where {
- my ($person, $required_years_old) = @_;
- return $person->years_old > $required_years_old;
- }
+ subtype PersonOverAge,
+ as Parameterizable[Person, RequiredAgeInYears]
+ where {
+ my ($person, $required_years_old) = @_;
+ return $person->years_old > $required_years_old;
+ }
This would validate the following:
- my $person = Person->new(age=>35);
- PersonOverAge([18])->check($person);
+ my $person = Person->new(age=>35);
+ PersonOverAge([18])->check($person);
You can then apply the following coercion
- coerce PersonOverAge,
- from Dict[age=>int],
- via {Person->new(%$_)},
- from Int,
- via {Person->new(age=>$_)};
+ coerce PersonOverAge,
+ from Dict[age=>int],
+ via {Person->new(%$_)},
+ from Int,
+ via {Person->new(age=>$_)};
This coercion would then apply to all the following:
- PersonOverAge([18])->check(30); ## via the Int coercion
- PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
+ PersonOverAge([18])->check(30); ## via the Int coercion
+ PersonOverAge([18])->check({age=>50}); ## via the Dict coercion
However, you are not allowed to place coercions on parameterizable types
that have had their constraining value filled, nor subtypes of such. For
example:
- coerce PersonOverAge[18],
- from DateTime,
- via {$_->years};
+ coerce PersonOverAge[18],
+ from DateTime,
+ via {$_->years};
That would generate a hard exception. This is a limitation for now until
I can devise a smarter way to cache the generated type constraints.
Lastly, the constraining value is available in the coercion in much the
same way it is available to the constraint.
- ## Create a type constraint where a Person must be in the set
- subtype PersonInSet,
- as Parameterizable[Person, PersonSet],
- where {
- my ($person, $person_set) = @_;
- $person_set->find($person);
- }
-
- coerce PersonInSet,
- from HashRef,
- via {
- my ($hashref, $person_set) = @_;
- return $person_set->create($hash_ref);
- };
+ ## Create a type constraint where a Person must be in the set
+ subtype PersonInSet,
+ as Parameterizable[Person, PersonSet],
+ where {
+ my ($person, $person_set) = @_;
+ $person_set->find($person);
+ }
+
+ coerce PersonInSet,
+ from HashRef,
+ via {
+ my ($hashref, $person_set) = @_;
+ return $person_set->create($hash_ref);
+ };
Recursion
- TBD
+ TBD
TYPE CONSTRAINTS
This type library defines the following constraints.