Type::Tiny Tricks #6: Tricks with Tuples
Let's say you want an attribute to accept a pair of numbers - perhaps a geographic co-ordinates
[ 50.873, -0.002 ]. You could constrain the attribute as
ArrayRef[Num], but that would accept an arrayref containing a single number, or eight numbers, or even a reference to an empty array.
Tuple type constraint, you can be more exact in expressing which values are acceptable:
isa => Tuple[ Num, Num ]
Now let's say we want to optionally accept a third number; an altitude, in metres, which should be an integer. If the altitude is unknown, then it may be omitted. That's possible too:
isa => Tuple[ Num, Num, Optional[Int] ]
OK, let's forget altitude now, and assume we want to accept a pair of co-ordinates followed by zero or more strings indicating a label for the location:
[ 50.873, -0.002, "Lewes", "East Sussex", "United Kingdom" ]
The list of strings is variable length, but Tuples can cope with that too:
isa => Tuple[ Num, Num, slurpy ArrayRef[Str] ]
slurpy part conceptually slurps the remaining items of the arrayref into a new temporary array, and validates that against the
ArrayRef[Str] constraint. Checking a value against this constraint is roughly equivalent to:
my ($temp1, $temp2, @slurpy) = @$value; Num->assert_valid($temp1); Num->assert_valid($temp2); ArrayRef->of(Str)->assert_valid(\@slurpy);
As well as validating arrayrefs containing a mixture of different datatypes,
Tuple can be used to validate more homogenous arrays. For example
Tuple[Int, slurpy ArrayRef[Int]] is equivalent to
ArrayRef[Int] except that it requires at least one item in the array.
ArrayRef[Int] & Tuple[Any, slurpy Any] does the same, but might be more efficient because
Any is very optimized.
What about if you want to accept an arrayref of integers which is typically many thousands of integers long. You might like to save time by only validating that the first five items in the arrayref are integers. The rest we'll accept on trust:
Tuple[ (Optional[Int]) x 5, slurpy Any ]
Tuple type constraint is useful for all kinds of things. It would be useful to have an equivalently flexible constraint for hashrefs, wouldn't it? We'll cover that in the next tip.