From: John Napiorkowski Date: Wed, 23 Jun 2010 19:25:05 +0000 (-0400) Subject: added readme X-Git-Tag: 0.02~13 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=gitmo%2FMooseX-Dependent.git;a=commitdiff_plain;h=c08b66adbe12436280c1be2b31942daec47091bf added readme --- diff --git a/README b/README new file mode 100644 index 0000000..fe771eb --- /dev/null +++ b/README @@ -0,0 +1,265 @@ +NAME + MooseX::Types::Parameterizable - Create your own Parameterizable Types. + +SYNOPSIS + Within your MooseX::Types declared library module: + + use MooseX::Types::Parameterizable qw(Parameterizable); + + subtype Set, + as class_type("Set::Scalar"); + + subtype UniqueInt, + as Parameterizable[Int, Set], + where { + 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); + + UniqueInt([$negative_set])->check(100); ## Throws exception + +DESCRIPTION + A MooseX::Types library for creating parameterizable types. A + parameterizable type constraint for all intents and uses is a subclass + of a parent type, but adds a secondary type parameter which is available + to constraint callbacks (such as inside the 'where' clause) or in the + coercions. + + This allows you to create a type that has additional runtime advice, + 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 + + This throws a hard Moose exception. You'll need to capture it in an eval + or related exception catching system (see TryCatch). + + 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); + } + + 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! + + This is the preferred syntax, as it improve readability and adds to the + conciseness of your type constraint declarations. An exception wil be + thrown if your type parameters don't match the required reference type. + + Also not that if you 'chain' parameterization results with a method call + like: + + 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 + of "->". Patches or thoughts welcomed. You only need to do this in the + above case which I imagine is not a very common case. + + ==head2 Subtyping a Parameterizable type constraints + + When subclassing a parameterizable type you must be careful to match + either the required type parameter type constraint, or if + 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}); + }; + + Example subtype with additional constraints: + + 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]; + + Notice how re-parameterizing the parameterizable type 'RangedInt' works + slightly differently from re-parameterizing 'PositiveRange' Although it + initially takes two type constraint values to declare a parameterizable + type, should you wish to later re-parameterize it, you only use a + subtype of the second type parameter (the parameterizable type + constraint) since the first type constraint sets the parent type for the + parameterizable type. In other words, given the example above, a type + constraint of 'RangedInt' would have a parent of 'Int', not + '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], + ]; + + One caveat is that you can't stick an unparameterized parameterizable + type inside a structure, such as MooseX::Types::Structured since that + would require the ability to convert a 'containing' type constraint into + a parameterizable type, which is a capacity we current don't have. + + Coercions + Parameterizable types have some limited support for coercions. Several + things must be kept in mind. The first is that the coercion targets the + type constraint which is being made parameterizable, Not the + parameterizable type. So for example if you create a Parameterizable + type like: + + subtype RequiredAgeInYears, + as Int; + + 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); + + You can then apply the following coercion + + 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 + + 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}; + + 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. + However, I doubt it will be a significant limitation, since the general + use case is supported. + + 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); + }; + + Recursion + TBD + +TYPE CONSTRAINTS + This type library defines the following constraints. + + Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint] + Create a subtype of ParentTypeConstraint with a dependency on a value + that can pass the ParameterizableValueTypeConstraint. If + ParameterizableValueTypeConstraint is empty we default to the 'Any' type + constraint (see Moose::Util::TypeConstraints). + + This creates a type constraint which must be further parameterized at + later time before it can be used to ->check or ->validate a value. + Attempting to do so will cause an exception. + +AUTHOR + John Napiorkowski, "" + +COPYRIGHT & LICENSE + This program is free software; you can redistribute it and/or modify it + under the same terms as Perl itself. +