NAME
Aion::Type - class of validators
SYNOPSIS
use Aion::Type;
use Aion::Types qw//;
my $Int = Aion::Type->new(name => "Int", test => sub { /^-?\d+$/ });
12 ~~ $Int # => 1
12.1 ~~ $Int # -> ""
my $Char = Aion::Type->new(name => "Char", test => sub { /^.\z/ });
$Char->include("a") # => 1
$Char->exclude("ab") # => 1
my $IntOrChar = $Int | $Char;
77 ~~ $IntOrChar # => 1
"a" ~~ $IntOrChar # => 1
"ab" ~~ $IntOrChar # -> ""
my $Digit = $Int & $Char;
7 ~~ $Digit # => 1
77 ~~ $Digit # -> ""
"a" ~~ ~$Int; # => 1
5 ~~ ~$Int; # -> ""
eval { $Int->validate("a", "..Eval..") }; $@ # ~> ..Eval.. must have the type Int. The it is 'a'
DESCRIPTION
Spawns validators. Used in Aion::Types::subtype.
METHODS
new (%ARGUMENTS)
Constructor.
ARGUMENTS
name (Str) — Type name.
args (ArrayRef) — List of type arguments.
init (CodeRef) — Type initializer.
test (CodeRef) - Checker.
a_test (CodeRef) — Value checker for types with optional arguments.
coerce (ArrayRef[Tuple[Aion::Type, CodeRef]]) - Array of pairs: type and transition.
stringify
String conversion of object (name with arguments):
my $Char = Aion::Type->new(name => "Char");
$Char->stringify # => Char
my $Int = Aion::Type->new(
name => "Int",
args => [3, 5],
);
$Int->stringify #=> Int[3, 5]
Operations are also converted to a string:
($Int & $Char)->stringify # => ( Int[3, 5] & Char )
($Int | $Char)->stringify # => ( Int[3, 5] | Char )
(~$Int)->stringify # => ~Int[3, 5]
Operations are Aion::Type objects with special names:
Aion::Type->new(name => "Exclude", args => [$Char])->stringify # => ~Char
Aion::Type->new(name => "Union", args => [$Int, $Char])->stringify # => ( Int[3, 5] | Char )
Aion::Type->new(name => "Intersection", args => [$Int, $Char])->stringify # => ( Int[3, 5] & Char )
test
Tests that $_ belongs to a class.
my $PositiveInt = Aion::Type->new(
name => "PositiveInt",
test => sub { /^\d+$/ },
);
local $_ = 5;
$PositiveInt->test # -> 1
local $_ = -6;
$PositiveInt->test # -> ""
init
Validator initializer.
my $Range = Aion::Type->new(
name => "Range",
args => [3, 5],
init => [sub {
@{$Aion::Type::SELF}{qw/min max/} = @{$Aion::Type::SELF->{args}};
}],
test => sub { $Aion::Type::SELF->{min} <= $_ && $_ <= $Aion::Type::SELF->{max} },
);
$Range->init;
3 ~~ $Range # -> 1
4 ~~ $Range # -> 1
5 ~~ $Range # -> 1
2 ~~ $Range # -> ""
6 ~~ $Range # -> ""
include ($element)
Checks whether the argument belongs to the class.
my $PositiveInt = Aion::Type->new(
name => "PositiveInt",
test => sub { /^\d+$/ },
);
$PositiveInt->include(5) # -> 1
$PositiveInt->include(-6) # -> ""
exclude ($element)
Checks that the argument does not belong to the class.
my $PositiveInt = Aion::Type->new(
name => "PositiveInt",
test => sub { /^\d+$/ },
);
$PositiveInt->exclude(5) # -> ""
$PositiveInt->exclude(-6) # -> 1
coerce ($value)
Cast $value to type if the cast from type and function is in $self->{coerce}.
Corresponds to the >> operator.
my $Int = Aion::Type->new(name => "Int", test => sub { /^-?\d+\z/ });
my $Num = Aion::Type->new(name => "Num", test => sub { /^-?\d+(\.\d+)?\z/ });
my $Bool = Aion::Type->new(name => "Bool", test => sub { /^(1|0|)\z/ });
push @{$Int->{coerce}}, [$Bool, sub { 0+$_ }];
push @{$Int->{coerce}}, [$Num, sub { int($_+.5) }];
$Int->coerce(5.5) # => 6
$Int->coerce(undef) # => 0
$Int->coerce("abc") # => abc
detail ($element, $feature)
Generates an error message.
my $Int = Aion::Type->new(name => "Int");
$Int->detail(-5, "Feature car") # => Feature car must have the type Int. The it is -5!
my $Num = Aion::Type->new(name => "Num", message => sub {
"Error: $_ is'nt $Aion::Type::SELF->{property}!"
});
$Num->detail("x", "car") # => Error: x is'nt car!
validate ($element, $feature)
Checks $element and throws a detail message if the element does not belong to the class.
my $PositiveInt = Aion::Type->new(
name => "PositiveInt",
test => sub { /^\d+$/ },
);
eval {
$PositiveInt->validate(-1, "Neg")
};
$@ # ~> Neg must have the type PositiveInt. The it is -1
val_to_str ($val)
Converts $val to a string.
Aion::Type->new->val_to_str([1,2,{x=>6}]) # => [1, 2, {x => 6}]
instanceof ($type)
Determines that a type is a subtype of another $type by type name.
Doesn't work in | and ~. Doesn't check arguments.
my $Int = Aion::Type->new(name => "Int");
my $PositiveInt = Aion::Type->new(name => "PositiveInt", as => $Int);
$PositiveInt->instanceof('Int'); # -> 1
$PositiveInt->instanceof('PositiveInt'); # -> 1
$Int->instanceof('PositiveInt'); # -> ""
my $MyEnum = Aion::Type->new(name => "MyEnum", args => [3, 5, 'car']);
($MyEnum & $PositiveInt)->instanceof('Int'); # -> 1
is_set_theoretic
Checks that the type is set-theoretic (ie - the |, & or ~ operator).
simplify
If the expression has no values, it will return ~Any, otherwise it will return the expression.
Simplification of the expression in this function may appear in the future.
package Aion::Types;
my $type = (Enum[1,2] | Enum[2,3]) & Enum[2,3,4];
$type->simplify->stringify # => ( ( Enum[1, 2] | Enum[2, 3] ) & Enum[2, 3, 4] )
my $range = Range[-10,0] & Range[4,8];
$range->simplify->stringify # => ~Any
Any
A constant for a type that includes all values.
package Aion::Type;
42 ~~ Any # -> 1
42 ~~ None # -> ""
Any <= Any # -> 1
None <= Any # -> 1
Any <= None # -> ""
None
Constant for an empty type that does not contain anything.
identical ($type)
Types are equal if they have the same prototype (coerce), the same number of arguments, parent element, their arguments, and M and N are equal.
my $Int = Aion::Type->new(name => "Int");
my $PositiveInt = Aion::Type->new(name => "PositiveInt", as => $Int);
my $AnotherInt = Aion::Type->new(name => "Int", coerce => $Int->{coerce});
my $IntWithArgs = Aion::Type->new(name => "Int", args => [1, 2]);
my $AnotherIntWithArgs = Aion::Type->new(name => "Int", args => [1, 2], coerce => $IntWithArgs->{coerce});
my $IntWithDifferentArgs = Aion::Type->new(name => "Int", args => [3, 4]);
my $Str = Aion::Type->new(name => "Str");
$Int->identical($Int) # -> 1
$Int->identical($AnotherInt) # -> 1
$IntWithArgs->identical($AnotherIntWithArgs) # -> 1
$PositiveInt->identical($PositiveInt) # -> 1
$Int->{coerce} == $Str->{coerce} # -> ""
$Int->identical($Str) # -> ""
$Int->identical($IntWithArgs) # -> ""
$IntWithArgs->identical($IntWithDifferentArgs) # -> ""
$PositiveInt->identical($Int) # -> ""
$Int->identical("not a type") # -> ""
my $PositiveInt2 = Aion::Type->new(name => "PositiveInt", as => $Str);
$PositiveInt->identical($PositiveInt2) # -> ""
$Int->identical($PositiveInt) # -> ""
$PositiveInt->identical($Int) # -> ""
my $PositiveIntWithArgs = Aion::Type->new(name => "PositiveInt", as => $Int, args => [1]);
my $PositiveIntWithArgs2 = Aion::Type->new(name => "PositiveInt", as => $Int, args => [2]);
$PositiveIntWithArgs->identical($PositiveIntWithArgs2) # -> ""
distinct ($type)
Reverse operation to identical.
my $Int = Aion::Type->new(name => "Int");
my $PositiveInt = Aion::Type->new(name => "PositiveInt", as => $Int);
$Int->distinct($PositiveInt) # -> 1
$Int ne $PositiveInt # -> 1
disjoint ($other)
A type does not overlap with another type.
subset ($type)
Specifies that it is a subset of the specified type.
superset ($type)
Specifies that it is a superset of the specified type.
subproper ($other)
A type is a strict subset of another.
superproper ($other)
A type is a strict superset of another.
equals ($other)
A type is equivalent to another type.
differs ($other)
A type is not equivalent to another type.
disjoint ($other)
A type has no overlap with another type.
intersects ($other)
A type has an intersection or intersections with another type.
make ($pkg)
Creates a subroutine with no arguments that returns a type.
BEGIN {
Aion::Type->new(name=>"Rim", test => sub { /^[IVXLCDM]+$/i })->make(__PACKAGE__);
}
"IX" ~~ Rim # => 1
If init is specified, then each time the subroutine is used, a type will be created and initialized.
eval { Aion::Type->new(name=>"Rim", init => sub {...})->make(__PACKAGE__) }; $@ # ~> init_where won't work in Rim
If the routine cannot be created, an exception is thrown.
eval { Aion::Type->new(name=>"Rim")->make }; $@ # ~> syntax error
make_arg ($pkg)
Creates a subroutine with arguments that returns a type.
BEGIN {
Aion::Type->new(name=>"Len", test => sub {
$Aion::Type::SELF->{args}[0] <= length($_) && length($_) <= $Aion::Type::SELF->{args}[1]
})->make_arg(__PACKAGE__, 1);
}
"IX" ~~ Len[2,2] # => 1
If the routine cannot be created, an exception is thrown.
eval { Aion::Type->new(name=>"Rim")->make_arg }; $@ # ~> syntax error
make_maybe_arg ($pkg)
Creates a subroutine with or without arguments.
BEGIN {
Aion::Type->new(
name => "Enum123",
test => sub { $_ ~~ [1,2,3] },
a_test => sub { $_ ~~ $Aion::Type::SELF->{args} },
)->make_maybe_arg(__PACKAGE__);
}
3 ~~ Enum123 # -> 1
3 ~~ Enum123[4,5,6] # -> ""
5 ~~ Enum123[4,5,6] # -> 1
If the routine cannot be created, an exception is thrown.
eval { Aion::Type->new(name=>"Rim")->make_maybe_arg }; $@ # ~> syntax error
args ()
List of arguments.
name ()
Type name.
as ()
Parent type.
message (;&message)
Message accessor. Uses &message to generate an error message.
title (;$title)
Header accessor (used to create the swagger schema).
description (;$description)
Description accessor (used to create a swagger schema).
example (;$example)
Example accessor (used to create the swagger schema).
true ()
Always returns 1. Needed to specify a test for a type without where.
clone ()
Clone type.
my $type = Aion::Type->new(name => 'New');
my $type10 = $type->clone(args => [10]);
$type->stringify # => New
$type10->stringify # => New[10]
is_primitive ()
This is a primitive type, that is, one in whose hierarchy there are no set-theoretic operators.
Aion::Types::Int->is_primitive # -> 1
Aion::Types::Like->is_primitive # -> ""
is_union ()
This is a union of types.
Aion::Types::Int->is_union # -> ""
(Aion::Types::Int | Aion::Types::Int)->is_union # -> 1
is_intersection ()
This is the intersection of types.
Aion::Types::Int->is_intersection # -> ""
(Aion::Types::Int & Aion::Types::Int)->is_intersection # -> 1
is_exclude ()
This is a type exception.
Aion::Types::Any->is_exclude # -> ""
(~Aion::Types::Any)->is_exclude # -> 1
Aion::Types::None->is_exclude # -> 1
~Aion::Types::Any eq Aion::Types::None # -> 1
is_enum ()
This is an enumeration.
Aion::Types::Int->is_enum # -> ""
Aion::Types::Enum([1])->is_enum # -> 1
is_range_type ()
This is an interval type.
Aion::Types::Int->is_range_type # -> ""
Aion::Types::Len([10])->is_range_type # -> 1
range_lbound ()
Lower limit of the interval.
Aion::Types::Int->range_lbound # -> undef
Aion::Types::Len([10])->range_lbound # -> 0
Aion::Types::Range([0, 10])->range_lbound # -> '-Inf'
is_range ()
This is an interval.
Aion::Types::Int->is_range # -> ""
Aion::Types::Len([10])->is_range # -> ""
Aion::Types::Range([1, 10])->is_range # -> 1
typed_sorted_args_key ()
Generates a key with sorted typed parameters.
(Aion::Types::Int & Aion::Types::Num)->typed_sorted_args_key # -> (Aion::Types::Num & Aion::Types::Int)->typed_sorted_args_key
sorted_args_key ()
Generates a key with sorted untyped parameters.
Aion::Types::Enum([10, 20])->sorted_args_key # -> Aion::Types::Enum([20, 10])->sorted_args_key
key ()
A unique key from the type prototype and its parameters.
keyfn ($fn)
Sets/returns the key construction function for the type as a class.
my $type = Aion::Type->new(name => 'New', args => [10, 20]);
$type->keyfn($type->can('sorted_args_key'));
my $type2 = Aion::Type->new(name => 'New', args => [20, 10], coerce => $type->{coerce});
$type->key # -> $type2->key
asen ()
Returns the chain of ancestors.
[Aion::Types::Num->asen] # --> [Aion::Types::Any, Aion::Types::Item, Aion::Types::Defined, Aion::Types::Value, Aion::Types::Str]
ckey ()
Key for comparing types in <=> and cmp.
compare ($other)
Comparison for sorting. Used in the <=> and cmp operators.
is_descendant ($other, $is_strict)
A is a child of B. The prototype is compared, but if $is_strict is specified, then the eq operator is used.
Aion::Types::Range([1, 10])->is_descendant(Aion::Types::Defined) # -> 1
Aion::Types::Range([1, 10])->is_descendant(Aion::Types::Value) # -> ""
like ($other)
Compares with prototypes.
Aion::Types::Range([1, 10])->like(Aion::Types::Range([100, 200])) # -> 1
joint ($other)
Types overlap.
Aion::Types::Range([1, 10])->joint(Aion::Types::Range([100, 200])) # -> ""
Aion::Types::Range([1, 10])->joint(Aion::Types::Range([10, 200])) # -> 1
OPERATORS
&{}
Tests $_.
my $PositiveInt = Aion::Type->new(
name => "PositiveInt",
test => sub { /^\d+$/ },
);
local $_ = 10;
$PositiveInt->() # -> 1
$_ = -1;
$PositiveInt->() # -> ""
""
Strings an object.
Aion::Type->new(name => "Int") . "" # => Int
my $Enum = Aion::Type->new(name => "Enum", args => [qw/A B C/]);
"$Enum" # => Enum['A', 'B', 'C']
|
Or. Creates a new type as a union of two.
my $Int = Aion::Type->new(name => "Int", test => sub { /^-?\d+$/ });
my $Char = Aion::Type->new(name => "Char", test => sub { /^.\z/ });
my $IntOrChar = $Int | $Char;
77 ~~ $IntOrChar # -> 1
"a" ~~ $IntOrChar # -> 1
"ab" ~~ $IntOrChar # -> ""
&
I. Creates a new type as the intersection of two.
my $Int = Aion::Type->new(name => "Int", test => sub { /^-?\d+$/ });
my $Char = Aion::Type->new(name => "Char", test => sub { /^.\z/ });
my $Digit = $Int & $Char;
7 ~~ $Digit # -> 1
77 ~~ $Digit # -> ""
"a" ~~ $Digit # -> ""
~
Not. Creates a new type as an exception to the given one.
my $Int = Aion::Type->new(name => "Int", test => sub { /^-?\d+$/ });
"a" ~~ ~$Int; # -> 1
5 ~~ ~$Int; # -> ""
~~
Tests the value.
my $Int = Aion::Type->new(name => "Int", test => sub { /^-?\d+$/ });
$Int ~~ 3 # -> 1
-6 ~~ $Int # -> 1
>>
Casting to type.
my $Int = Aion::Type->new(name => "Int", test => sub { /^-?\d+$/ });
$Int->{coerce} = [[$Int => sub { $_ + 5 }]];
5 >> $Int # -> 10
$Int >> -4 # -> 1
eq
The types are identical.
my $Int1 = Aion::Type->new(name => "Int1");
my $Int2 = Aion::Type->new(name => "Int2", coerce => $Int1->{coerce});
$Int1 eq $Int2; # -> 1
delete $Int1->{key};
$Int1->{M} = 2;
$Int1 eq $Int2; # -> ""
my $Enum1 = Aion::Type->new(name => "Enum", args => ['red', 'green']);
my $Enum2 = Aion::Type->new(name => "Enum", args => ['green', 'red'], coerce => $Enum1->{coerce});
$Enum1->keyfn(\&Aion::Type::sorted_args_key);
$Enum1 eq $Enum2 # -> 1
$Enum1->key eq $Enum2->key # -> 1
ne
The types are different.
==
Equivalence of two types.
my $Enum1 = Aion::Type->new(name => "Enum", args => ['red', 'green']);
my $Enum2 = Aion::Type->new(name => "Enum", args => ['green'], coerce => $Enum1->{coerce});
my $Enum3 = Aion::Type->new(name => "Enum", args => ['red'], coerce => $Enum1->{coerce});
$Enum1 == ($Enum2 | $Enum3) # -> 1
$Enum1 eq ($Enum2 | $Enum3) # -> ""
!=
Non-equivalence of two types. The operation is the opposite of equivalence.
<
A is a strict subset of B.
my $Num = Aion::Type->new(name => "Num");
my $Int = Aion::Type->new(name => "Int", as => $Num);
my $Str = Aion::Type->new(name => "Str");
$Int < $Num # -> 1
$Int < ($Int | $Str) # -> 1
$Int < ($Num | $Str) # -> 1
$Num < $Int # -> ""
$Int < $Int # -> ""
($Num | $Str) < $Int # -> ""
>
A is a strict superset of B.
<=
A is a subset of B.
>=
A is a superset of B.
<=>
Comparison of two types. Used for sorting.
package Aion::Types;
Enum[1,2] <=> Enum[1,2,3] # -> 1
Enum[1,2,3] <=> Enum[1,2] # -> -1
Enum[1,2] <=> Enum[1,2] # -> 0
Range[1,5] <=> Range[1,10] # -> 1
Range[1,10] <=> Range[1,5] # -> -1
Range[1,5] <=> Range[1,5] # -> 0
Int <=> Num # -> 1
Num <=> Int # -> -1
Str <=> Int # -> -1
cmp
Similar to <=>.
AUTHOR
Yaroslav O. Kosmina mailto:dart@cpan.org
LICENSE
⚖ GPLv3
COPYRIGHT
The Aion::Type module is copyright © 2023 Yaroslav O. Kosmina. Rusland. All rights reserved.