Aion

 view release on metacpan or  search on metacpan

lib/Aion/Types.pm  view on Meta::CPAN

package Aion::Types;
# Типы-валидаторы для Aion

use common::sense;
use warnings FATAL => 'recursion';

use Aion::Meta::Util qw/subref_is_reachable val_to_str/;
use Aion::Type;
use Aion::Type::Lim;
use List::Util qw/all any first/;
use Exporter qw/import/;
require overload;
use POSIX qw//;
use Scalar::Util qw/looks_like_number reftype refaddr blessed/;
use Sub::Util qw//;

our @EXPORT = our @EXPORT_OK = grep {
	eval {*{$Aion::Types::{$_}}{CODE}}	&& !/^(_|(NaN|import|all|any|first|looks_like_number|reftype|refaddr|blessed|subref_is_reachable|val_to_str|DBL_MAX)\z)/n
} keys %Aion::Types::;

# Обрабатываем атрибут :Isa
sub MODIFY_CODE_ATTRIBUTES {
    my ($pkg, $referent, @attributes) = @_;

    grep { /^Isa\((.*)\)\z/s? do { _Isa($pkg, $referent, $1); 0 }: 1 } @attributes
}

sub _Isa {
	my ($pkg, $referent, $data) = @_;
	my $subname = Sub::Util::subname $referent;
	$subname =~ s/^.*:://;

	die "Anonymous subroutine cannot use :Isa!" if $subname eq '__ANON__';
	
	my @signature = eval "package $pkg; map { UNIVERSAL::isa(\$_, 'Aion::Type')? \$_: __PACKAGE__->can(\$_)? __PACKAGE__->can(\$_)->(): Aion::Types::External([\$_]) } ($data)";
	die if $@;

	die "$pkg\::$subname has no return type!" if @signature == 0;

	require Aion::Meta::Subroutine;
	my $subroutine = Aion::Meta::Subroutine->new(
		pkg => $pkg,
		subname => $subname,
		signature => \@signature,
		referent => $referent,
	);
	
	if(!subref_is_reachable($referent)) {
		$Aion::META{$pkg}{require}{$subname} = $subroutine;
	} else {
		my $require = delete $Aion::META{$pkg}{require}{$subname};
		$require->compare($subroutine) if $require;

		my $overload = $Aion::META{$pkg}{subroutine}{$subname};
		$overload->compare($subroutine) if $overload;
		
		$subroutine->wrap_sub;
	}	
}

BEGIN {
my $INIT_ARGS = sub { @{&ARGS} = map External([$_]), &ARGS };
my $INIT_KW_ARGS = sub { @{&ARGS} = List::Util::pairmap { $a => External([$b]) } &ARGS };

my $COMBINE_SUBS = sub {
    my ($f1, $f2) = @_;
    sub { $f1->(); $f2->() }
};

my $COMBINE_WHERE = sub {
    my ($f1, $f2) = @_;
    sub { $f1->() && $f2->() }
};

my $IS_PARAM = sub {
	my @S = @_;
	while(@S) {
		my $arg = pop @S;
		return 1 if UNIVERSAL::isa($arg, 'Aion::Type') && $arg->{is_param};
		push @S, @{$arg->{args}};
	}
	""
};

my $REPLACE_PARAM; $REPLACE_PARAM = sub {
	my ($arg) = @_;

	return $arg unless UNIVERSAL::isa($arg, 'Aion::Type');

	if(my $param = $arg->{is_param}) {
		return $Aion::Type::SELF->{args}->[$param - 1] if $param > 0;
		return $Aion::Type::SELF->{N} if $param == -1;
		return $Aion::Type::SELF->{M} if $param == -2;
		return $Aion::Type::SELF if $param == -256;
		return @{$Aion::Type::SELF->{args}} if $param == -1024;
		die "Parameter number invalid!";
	}

	return $arg if !$arg->{args} || !List::Util::first { UNIVERSAL::isa($_, 'Aion::Type') } @{$arg->{args}};

	$arg = bless {%$arg}, 'Aion::Type';
	$arg->{args} = [map $REPLACE_PARAM->($_), @{$arg->{args}}];
	$arg->init if $arg->{init};

	$arg
};

my $INIT_REPLACE_PARAM = sub {
	$Aion::Type::SELF->{as} = $REPLACE_PARAM->($Aion::Type::SELF->{as});
};

# Создание типа
sub subtype(@) {
	my $subtype = shift;
	my %o = @_;
	
	my ($as, $init_where, $where, $awhere, $message) = delete @o{qw/as init_where where awhere message/};
	
	die "subtype $subtype unused keys left: " . join ", ", keys %o if keys %o;
	
	die "subtype format is Name or Name[args] or Name`[args]" if $subtype !~ /^([A-Z_]\w*)(?:(\`)?\[(.*)\])?$/i;
	my ($name, $is_maybe_arg, $is_arg) = ($1, $2, $3);

	my $pkg = scalar caller;
	die "subtype $subtype: ${pkg}::$name exists!" if *{"${pkg}::$name"}{CODE};

	if($is_maybe_arg) {
		die "subtype $subtype: needs an awhere" if !$awhere;
	} else {
		die "subtype $subtype: awhere is excess" if $awhere;
	}

	my @init = $init_where? $init_where: ();
	
	my $init_types = do { given($is_arg) {
		$INIT_ARGS when /^[A-Z]\w*(,\s*[A-Z]\w*)?\.\.\.$/;
		$INIT_KW_ARGS when /^[a-z]\w*\s*=>\s*[A-Z],?\s*\.\.\.$/;
		when(/\b[A-Z]\b/) {
			my @args = split /\s*,\s*/, $is_arg;
			my @typeno = grep { $args[$_] =~ /^[A-Z]/ } 0..@args-1;
			(sub { my ($typeno) = @_; sub {
				my $args = &ARGS;
				$args->[$_] = External([$args->[$_]]) for @$typeno;
			} })->(\@typeno);
		}
	}};

	unshift @init, $init_types if $init_types;
	
	$as = External([$as]) if defined $as;
	
	unshift @init, $INIT_REPLACE_PARAM if $as && $is_arg && $IS_PARAM->($as);

	# Тут coerce - прототип - единый для всех порождаемых типов одного типа с разными аргументами
	my $type = Aion::Type->new(
		name => $name,
		coerce => [], # prototype
		test => $where // \&Aion::Type::true,
		$as? (as => $as): (),
		@init? (init => \@init): (),
		$awhere? (a_test => $awhere): (),

lib/Aion/Types.pm  view on Meta::CPAN

				subtype "RegexpRef", as &Ref, where { ref $_ eq "Regexp" };
				subtype "ValueRef`[A]", as &Ref,
					where { ref($_) ~~ ["SCALAR", "REF"] }
					awhere { ref($_) ~~ ["SCALAR", "REF"] && A->include($$_) };
					subtype "ScalarRef`[A]", as &ValueRef,
						where { ref $_ eq "SCALAR" }
						awhere { ref $_ eq "SCALAR" && A->include($$_) };
					subtype "RefRef`[A]", as &ValueRef,
						where { ref $_ eq "REF" }
						awhere { ref $_ eq "REF" && A->include($$_) };
				subtype "GlobRef", as &Ref, where { ref $_ eq "GLOB" };
					subtype "FileHandle", as &GlobRef,
						where { !!*$_{IO} };
				subtype "ArrayRef`[A]", as &Ref,
					where { ref $_ eq "ARRAY" }
					awhere { my $A = A; ref $_ eq "ARRAY" && all { $A->test } @$_ };
					subtype "Tuple[A...]", as &ArrayRef,
						where {
							my $k = 0;
							for my $A (ARGS) {
								return "" if $A->exclude($_->[$k++]);
							}
							$k == @$_
						};
					subtype "CycleTuple[A...]", as &ArrayRef,
						where {
							my $k = 0;
							while($k < @$_) {
								for my $A (ARGS) {
									return "" if $A->exclude($_->[$k++]);
								}
							}
							$k == @$_
						};
				subtype "HashRef`[A]", as &Ref,
					where { ref $_ eq "HASH" }
					awhere { my $A = A; ref $_ eq "HASH" && all { $A->test } values %$_ };
					subtype "Dict[k => A, ...]", as &HashRef,
						where {
							my $count = 0; my $k;
							for my $A (ARGS) {
								$k = $A, next unless ref $A;
								if(exists $_->{$k}) {
									return "" if $A->exclude($_->{$k});
									$count++;
								} else {
									return "" if !exists $A->{is_option};
								}
							}
							$count == keys %$_
						};
					subtype "Map[K, V]", as &HashRef,
						where {
							my ($K, $V) = ARGS;
							while(my ($k, $v) = each %$_) {
								return "" unless $K->include($k) && $V->include($v);
							}
							return 1;
						};
				subtype "Object`[class]", as &Ref,
					where { blessed($_) ne "" }
					awhere { blessed($_) && $_->isa(A) };
					subtype "Me", as &Object,
						init_where { SELF->{as} = Object([caller(2)]) };
					subtype "Rat", as 'Math::BigRat';
				subtype "RegexpLike", as &Ref,
					where { reftype($_) eq "REGEXP" || !!overload::Method($_, 'qr') };
				subtype "CodeLike", as &Ref,
					where { reftype($_) eq "CODE" || !!overload::Method($_, '&{}') };
				subtype "ArrayLike`[A]", as &Ref,
					where { reftype($_) eq "ARRAY" || !!overload::Method($_, '@{}') }
					awhere { &ArrayLike->test && do { my $A = A; all { $A->test } @$_ }};
					subtype "Lim[from, to?]", as &ArrayLike,
						init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
						where { A <= @$_ && @$_ <= B };
				subtype "HashLike`[A]", as &Ref,
					where { reftype($_) eq "HASH" || !!overload::Method($_, "%{}") }
					awhere { &HashLike->test && do { my $A = A; all { $A->test } values %$_ }};
						subtype "HasProp[p...]", as &HashLike,
							where { my $x = $_; all { exists $x->{$_} } ARGS };
						subtype "LimKeys[from, to?]", as &HashLike,
							init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
							where { A <= scalar keys %$_ && scalar keys %$_ <= B };
		
			subtype "Like", as &Str | &Object;
				subtype "HasMethods[m...]", as &Like,
					where { my $x = $_; all { $x->can($_) } ARGS };
				subtype "Overload`[m...]", as &Like,
					where { !!overload::Overloaded($_) }
					awhere { my $x = $_; all { overload::Method($x, $_) } ARGS };
				subtype "InstanceOf[class...]", as &Like, where { my $x = $_; all { $x->isa($_) } ARGS };
				subtype "ConsumerOf[role...]", as &Like, where { my $x = $_; all { $x->DOES($_) } ARGS };
				subtype "StrLike", as &Like, where { !blessed($_) or !!overload::Method($_, '""') };
					subtype "Len[from, to?]", as &StrLike,
						init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
						where { A <= length($_) && length($_) <= B };
	
				subtype "NumLike", as &Num | &Object & Overload(["0+"]);
					sub Opened($) { Aion::Type::Lim->from(ref $_[0] eq "ARRAY"? $_[0][0]: $_[0]) };
					subtype "Range[from, to]", as &NumLike,
						init_where {
							SELF->{args}[0] = A->inc if UNIVERSAL::isa(A, 'Aion::Type::Lim');
							SELF->{args}[1] = B->dec if UNIVERSAL::isa(B, 'Aion::Type::Lim');
						}
						where { A <= $_ && $_ <= B };
						subtype "Float", as Range([-(POSIX::FLT_MAX), POSIX::FLT_MAX]);
						subtype "Double", as Range([-(DBL_MAX), DBL_MAX]);
						subtype "Bytes[n]", as Range([]),
							init_where {
								my $_8bits = A < 8? 8: _8BITS;
								my $N = 1 << ($_8bits * A - 1);
								SELF->{as} = Range([-$N, $N-1]);
							};
						subtype "PositiveBytes[n]", as Range([]),
							init_where {
								my $_8bits = A < 8? 8: _8BITS;
								my $M = 1 << ($_8bits*A);
								SELF->{as} = Range([0, $M-1]);
							};

	coerce &Str => from &Undef => via { "" };
	coerce &Int => from &Num => via { int($_+($_ < 0? -.5: .5)) };
	coerce &Bool => from &Any => via { !!$_ };

	subtype 'Join[separator]', as &Str;
	coerce &Join, from &ArrayRef, via { join A, @$_ };

	subtype 'Split[separator]', as &ArrayRef;
	coerce &Split, from &Str, via { [split A, $_] };

	coerce &Rat => from &StrRat => via { Math::BigRat->new($_) };

	subtype "PositiveNum", as &Num & Range([0, 'Inf']);
	subtype "PositiveInt", as &Int & Range([0, 'Inf']);
	subtype "Nat", as &Int & Range([1, 'Inf']);

	my $_none = ~&Any;
	sub None() { $_none }
};

$_->keyfn(\&Aion::Type::typed_sorted_args_key) for Union[], Intersection[];
(Enum[])->keyfn(\&Aion::Type::sorted_args_key);

%Aion::Type::range_lbound = map { (Scalar::Util::refaddr $_->{coerce} => $_->{name} eq 'Range'? '-Inf': 0) } Range[], Lim[], LimKeys[], Len[];

1;

__END__

=encoding utf-8

=head1 NAME

lib/Aion/Types.pm  view on Meta::CPAN


Type value exception. Similar to the C<~$type> operator.

	-5  ~~ Exclude[PositiveInt] # -> 1
	"a" ~~ Exclude[PositiveInt] # -> 1
	5   ~~ Exclude[PositiveInt] # -> ""
	5.5 ~~ Exclude[PositiveInt] # -> 1

=head2 Option[A]

Additional keys in C<Dict>.

	{a=>55} ~~ Dict[a=>Int, b => Option[Int]]          # -> 1
	{a=>55, b=>31} ~~ Dict[a=>Int, b => Option[Int]]   # -> 1
	{a=>55, b=>31.5} ~~ Dict[a=>Int, b => Option[Int]] # -> ""

=head2 Wantarray[A, S]

If the routine returns different values in array and scalar contexts, then the C<Wantarray> type is used with type C<A> for the array context and type C<S> for the scalar context.

	sub arr : Isa(PositiveInt => Wantarray[ArrayRef[PositiveInt], PositiveInt]) {
		my ($n) = @_;
		wantarray? 1 .. $n: $n
	}
	
	my @array = arr(3);
	my $scalar = arr(3);
	
	\@array # --> [1,2,3]
	$scalar  # -> 3

=head2 Item

The top-level type in the hierarchy of scalar types.

=head2 External[type]

Sets C<type> to C<Aion::Type>.

=over

=item * If C<type> is C<Aion::Type>, then returns it unchanged.

=item * If C<type> is a string, then wraps it in C<Object>.

=item * If C<type> can be called, then wraps it in C<< Aion::Type-E<gt>new(test =E<gt> $type, ...) >>. And if it has a C<coerce> method, it will use it for transformations. Thanks to this, it is possible to use external types like C<Type::Tiny> in th...

=back

	External['Aion'] # -> Object['Aion']
	External[sub { /^x/ }] ~~ 'xyz' # -> 1
	
	package MyInt {
		use overload "&{}" => sub {
			sub { /^[+-]?[0-9]+$/ }
		};
		
		sub coerce { /\./? int($_): $_ }
	}
	
	my $myint = bless {}, 'MyInt';
	
	External([$myint]) ~~ '+123' # -> 1
	External([$myint])->coerce(10.1) # => 10
	External([$myint])->coerce('abc') # => abc

=head2 Bool

C<1> is true. C<0>, C<""> or C<undef> is false.

	1 ~~ Bool  # -> 1
	0 ~~ Bool  # -> 1
	undef ~~ Bool # -> 1
	"" ~~ Bool # -> 1
	
	2 ~~ Bool  # -> ""
	[] ~~ Bool # -> ""

=head2 Enum[A...]

Enumeration.

	3 ~~ Enum[1,2,3];            # -> 1
	"cat" ~~ Enum["cat", "dog"]; # -> 1
	4 ~~ Enum[1,2,3];            # -> ""
	
	Enum["cat"] <= Enum["cat", "dog"] # -> 1
	Enum["cat", "dog"] <= Enum["cat", "dog"] # -> 1
	
	Enum(["cat", "dog"])->disjoint(Enum[1]) # -> 1
	Enum(["cat", "dog"])->disjoint(Enum["ret", "cat"]) # -> ""
	
	Enum[1,2] <= Enum[1,2,3]          # -> 1
	Enum[1,2] <= Enum[2,3]            # -> ""
	Enum[1,2] == Enum[2,1]            # -> 1
	Enum[1,2] < Enum[1,2,3]           # -> 1
	Enum[1,2,3] > Enum[1,2]           # -> 1
	Enum[1,2] == (Enum[1] | Enum[2])  # -> 1

=head2 Maybe[A]

C<undef> or type in C<[]>.

	undef ~~ Maybe[Int] # -> 1
	4 ~~ Maybe[Int]     # -> 1
	"" ~~ Maybe[Int]    # -> ""

=head2 Undef

Only C<undef>.

	undef ~~ Undef # -> 1
	0 ~~ Undef     # -> ""

=head2 Defined

Everything except C<undef>.

	\0 ~~ Defined    # -> 1
	undef ~~ Defined # -> ""

lib/Aion/Types.pm  view on Meta::CPAN

Open border. Used with C<Range>.

=head2 Bytes[N]

Calculates the maximum and minimum numbers that will fit in C<N> bytes and checks the constraint between them.

	-129 ~~ Bytes[1] # -> ""
	-128 ~~ Bytes[1] # -> 1
	127 ~~ Bytes[1]  # -> 1
	128 ~~ Bytes[1]  # -> ""
	
	# 2 bits power of (8 bits * 8 bytes - 1)
	my $N = 1 << (8*3-1);
	(-$N-1) ~~ Bytes[3] # -> ""
	(-$N) ~~ Bytes[3]   # -> 1
	($N-1) ~~ Bytes[3]  # -> 1
	$N ~~ Bytes[3];     # -> ""
	
	require Math::BigInt;
	
	my $N17 = 1 << (8*Math::BigInt->new(17) - 1);
	
	((-$N17-1) . "") ~~ Bytes[17] # -> ""
	(-$N17 . "") ~~ Bytes[17]     # -> 1
	(($N17-1) . "") ~~ Bytes[17]  # -> 1
	($N17 . "") ~~ Bytes[17]      # -> ""

=head2 PositiveBytes[N]

Calculates the maximum number that will fit in C<N> bytes (assuming there is no negative bit in the bytes) and checks the limit from 0 to that number.

	-1 ~~ PositiveBytes[1]  # -> ""
	0 ~~ PositiveBytes[1]   # -> 1
	255 ~~ PositiveBytes[1] # -> 1
	256 ~~ PositiveBytes[1] # -> ""
	
	-1 ~~ PositiveBytes[8]   # -> ""
	1.01 ~~ PositiveBytes[8] # -> ""
	0 ~~ PositiveBytes[8]    # -> 1
	
	my $N8 = 2 ** (8*Math::BigInt->new(8)) - 1;
	
	$N8 . "" ~~ PositiveBytes[8]     # -> 1
	($N8+1) . "" ~~ PositiveBytes[8] # -> ""
	
	-1 ~~ PositiveBytes[17] # -> ""
	0 ~~ PositiveBytes[17]  # -> 1

=head2 Ref

Link.

	\1 ~~ Ref # -> 1
	[] ~~ Ref # -> 1
	1 ~~ Ref  # -> ""

=head2 Tied`[A]

Link to the associated variable.

	package TiedHash { sub TIEHASH { bless {@_}, shift } }
	package TiedArray { sub TIEARRAY { bless {@_}, shift } }
	package TiedScalar { sub TIESCALAR { bless {@_}, shift } }
	
	tie my %a, "TiedHash";
	tie my @a, "TiedArray";
	tie my $a, "TiedScalar";
	my %b; my @b; my $b;
	
	\%a ~~ Tied # -> 1
	\@a ~~ Tied # -> 1
	\$a ~~ Tied # -> 1
	
	\%b ~~ Tied  # -> ""
	\@b ~~ Tied  # -> ""
	\$b ~~ Tied  # -> ""
	\\$b ~~ Tied # -> ""
	
	ref tied %a     # => TiedHash
	ref tied %{\%a} # => TiedHash
	
	\%a ~~ Tied["TiedHash"]   # -> 1
	\@a ~~ Tied["TiedArray"]  # -> 1
	\$a ~~ Tied["TiedScalar"] # -> 1
	
	\%a ~~ Tied["TiedArray"]   # -> ""
	\@a ~~ Tied["TiedScalar"]  # -> ""
	\$a ~~ Tied["TiedHash"]    # -> ""
	\\$a ~~ Tied["TiedScalar"] # -> ""

=head2 LValueRef

The function allows assignment.

	ref \substr("abc", 1, 2) # => LVALUE
	ref \vec(42, 1, 2) # => LVALUE
	
	\substr("abc", 1, 2) ~~ LValueRef # -> 1
	\vec(42, 1, 2) ~~ LValueRef # -> 1

But it doesn't work with C<:lvalue>.

	sub abc: lvalue { $_ }
	
	abc() = 12;
	$_ # => 12
	ref \abc()  # => SCALAR
	\abc() ~~ LValueRef	# -> ""
	
	
	package As {
		sub x : lvalue {
			shift->{x};
		}
	}
	
	my $x = bless {}, "As";
	$x->x = 10;
	
	$x->x # => 10
	$x	# --> bless {x=>10}, "As"
	
	ref \$x->x			 # => SCALAR
	\$x->x ~~ LValueRef # -> ""

And on the end:

	\1 ~~ LValueRef	# -> ""
	
	my $x = "abc";
	substr($x, 1, 1) = 10;
	
	$x # => a10c
	
	LValueRef->include( \substr($x, 1, 1) )	# => 1

=head2 FormatRef

Format.

	format EXAMPLE_FMT =
	@<<<<<<   @||||||   @>>>>>>
	"left",   "middle", "right"
	.
	
	*EXAMPLE_FMT{FORMAT} ~~ FormatRef # -> 1
	\1 ~~ FormatRef	# -> ""

=head2 CodeRef

Subroutine.

	sub {} ~~ CodeRef # -> 1
	\1 ~~ CodeRef     # -> ""

=head2 NamedCode[name]

The subroutine with the specified name. C<name> – string or regular character.

	sub code_ex { ... }
	
	\&code_ex ~~ NamedCode['main::code_ex'] # -> 1
	\&code_ex ~~ NamedCode['code_ex']       # -> ""
	\&code_ex ~~ NamedCode[qr/_/]           # -> 1

=head2 ProtoCode[prototype]

A subroutine with the specified prototype.

	sub codex ($;$);
	
	\&codex ~~ ProtoCode['@']     # -> ""
	\&codex ~~ ProtoCode['$;$']   # -> 1
	\&codex ~~ ProtoCode[qr/^\$/] # -> 1

=head2 ForwardRef

Subroutine without body.

	sub code_ref {};
	sub code_forward;

lib/Aion/Types.pm  view on Meta::CPAN

	close $fh;
	
	opendir my $dh, ".";
	$dh ~~ FileHandle	         # -> 1
	closedir $dh;
	
	use constant { PF_UNIX => 1, SOCK_STREAM => 1 };
	
	socket my $sock, PF_UNIX, SOCK_STREAM, 0;
	$sock ~~ FileHandle	         # -> 1
	close $sock;

=head2 ArrayRef`[A]

Array references.

	[] ~~ ArrayRef	# -> 1
	{} ~~ ArrayRef	# -> ""
	[] ~~ ArrayRef[Num]	# -> 1
	{} ~~ ArrayRef[Num]	# -> ''
	[1, 1.1] ~~ ArrayRef[Num]	# -> 1
	[1, undef] ~~ ArrayRef[Num]	# -> ""

=head2 Lim[from?, to]

Limits arrays from C<from> or 0 to C<to> elements.

	[] ~~ Lim[5]     # -> 1
	[1..5] ~~ Lim[5] # -> 1
	[1..6] ~~ Lim[5] # -> ""
	
	[1..5] ~~ Lim[1,5] # -> 1
	[1..6] ~~ Lim[1,5] # -> ""
	
	[1] ~~ Lim[1,5] # -> 1
	[] ~~ Lim[1,5]  # -> ""
	
	Lim[0, 10] <= Lim[0, 20]        # -> 1
	Lim[10, 'Inf'] <= Lim[0, 'Inf'] # -> 1
	
	Lim[3] <= Lim[5]          # -> 1
	Lim[3] <= Lim[2]          # -> ""
	Lim[2,5] < Lim[1,6]       # -> 1
	Lim[2,5] == Lim[2,5]      # -> 1

=head2 HashRef`[H]

Links to hashes.

	{} ~~ HashRef # -> 1
	\1 ~~ HashRef # -> ""
	
	[]  ~~ HashRef[Int]           # -> ""
	{x=>1, y=>2}  ~~ HashRef[Int] # -> 1
	{x=>1, y=>""} ~~ HashRef[Int] # -> ""

=head2 Object`[O]

Blessed links.

	bless(\(my $val=10), "A1") ~~ Object # -> 1
	\(my $val=10) ~~ Object              # -> ""
	
	bless(\(my $val=10), "A1") ~~ Object["A1"] # -> 1
	bless(\(my $val=10), "A1") ~~ Object["B1"] # -> ""

=head2 Me

Blessed references to objects of the current package.

	package A1 {
		use Aion;
		bless({}, __PACKAGE__) ~~ Me  # -> 1
		bless({}, "A2") ~~ Me         # -> ""
	}

=head2 Map[K, V]

Like C<HashRef>, but with a key type.

	{} ~~ Map[Int, Int]               # -> 1
	{5 => 3} ~~ Map[Int, Int]         # -> 1
	+{5.5 => 3} ~~ Map[Int, Int]      # -> ""
	{5 => 3.3} ~~ Map[Int, Int]       # -> ""
	{5 => 3, 6 => 7} ~~ Map[Int, Int] # -> 1

=head2 Tuple[A...]

Tuple.

	["a", 12] ~~ Tuple[Str, Int]    # -> 1
	["a", 12, 1] ~~ Tuple[Str, Int] # -> ""
	["a", 12.1] ~~ Tuple[Str, Int]  # -> ""

=head2 CycleTuple[A...]

Tuple repeated one or more times.

	["a", -5] ~~ CycleTuple[Str, Int] # -> 1
	["a", -5, "x"] ~~ CycleTuple[Str, Int] # -> ""
	["a", -5, "x", -6] ~~ CycleTuple[Str, Int] # -> 1
	["a", -5, "x", -6.2] ~~ CycleTuple[Str, Int] # -> ""

=head2 Dict[k => A, ...]

Dictionary.

	{a => -1.6, b => "abc"} ~~ Dict[a => Num, b => Str] # -> 1
	
	{a => -1.6, b => "abc", c => 3} ~~ Dict[a => Num, b => Str] # -> ""
	{a => -1.6} ~~ Dict[a => Num, b => Str] # -> ""
	
	{a => -1.6} ~~ Dict[a => Num, b => Option[Str]] # -> 1

=head2 HasProp[p...]

A hash has the following properties. In addition to them, he may have others.

	[0, 1] ~~ HasProp[qw/0 1/] # -> ""
	
	{a => 1, b => 2, c => 3} ~~ HasProp[qw/a b/] # -> 1
	{a => 1, b => 2} ~~ HasProp[qw/a b/] # -> 1
	{a => 1, c => 3} ~~ HasProp[qw/a b/] # -> ""
	
	bless({a => 1, b => 3}, "A") ~~ HasProp[qw/a b/] # -> 1

=head2 LimKeys[from?, to]

Limits the number of keys in the hash from C<from> or 0 to C<to>.

	my %hash = (a => 1, b => 2);
	
	\%hash ~~ LimKeys[1] # -> ""
	\%hash ~~ LimKeys[2] # -> 1
	\%hash ~~ LimKeys[3] # -> 1
	\%hash ~~ LimKeys[3, 3] # -> ""
	\%hash ~~ LimKeys[2, 3] # -> 1
	\%hash ~~ LimKeys[2, 2] # -> 1
	
	LimKeys[20, 'Inf'] <= LimKeys[2, 'Inf'] # -> 1
	
	LimKeys[3] <= LimKeys[5]    # -> 1
	LimKeys[2,5] < LimKeys[1,6] # -> 1

=head2 Like

Object or string.

	"" ~~ Like # -> 1
	1 ~~ Like  # -> 1
	bless({}, "A") ~~ Like # -> 1
	bless([], "A") ~~ Like # -> 1
	bless(\(my $str = ""), "A") ~~ Like # -> 1
	\1 ~~ Like  # -> ""

=head2 HasMethods[m...]

An object or class has listed methods. In addition to them, there may be others.

	package HasMethodsExample {
		sub x1 {}
		sub x2 {}
	}
	
	"HasMethodsExample" ~~ HasMethods[qw/x1 x2/]			# -> 1
	bless({}, "HasMethodsExample") ~~ HasMethods[qw/x1 x2/] # -> 1
	bless({}, "HasMethodsExample") ~~ HasMethods[qw/x1/]	# -> 1
	"HasMethodsExample" ~~ HasMethods[qw/x3/]				# -> ""
	"HasMethodsExample" ~~ HasMethods[qw/x1 x2 x3/]			# -> ""
	"HasMethodsExample" ~~ HasMethods[qw/x1 x3/]			# -> ""

=head2 Overload`[op...]

An object or class with overloaded operators.

	package OverloadExample {
		use overload '""' => sub { "abc" };
	}
	
	"OverloadExample" ~~ Overload            # -> 1
	bless({}, "OverloadExample") ~~ Overload # -> 1
	"A" ~~ Overload                          # -> ""
	bless({}, "A") ~~ Overload               # -> ""

And the specified operators are overloaded.

	"OverloadExample" ~~ Overload['""'] # -> 1
	"OverloadExample" ~~ Overload['|']  # -> ""

=head2 InstanceOf[A...]

A class or object inherits classes from a list.

	package Animal {}
	package Cat { our @ISA = qw/Animal/ }
	package Tiger { our @ISA = qw/Cat/ }
	
	
	"Tiger" ~~ InstanceOf['Animal', 'Cat'] # -> 1
	"Tiger" ~~ InstanceOf['Tiger']         # -> 1
	"Tiger" ~~ InstanceOf['Cat', 'Dog']    # -> ""

=head2 ConsumerOf[A...]

A class or object has the specified roles.

	package NoneExample {}
	package RoleExample { sub DOES { $_[1] ~~ [qw/Role1 Role2/] } }
	
	'RoleExample' ~~ ConsumerOf[qw/Role1/] # -> 1
	'RoleExample' ~~ ConsumerOf[qw/Role2 Role1/] # -> 1
	bless({}, 'RoleExample') ~~ ConsumerOf[qw/Role3 Role2 Role1/] # -> ""
	
	'NoneExample' ~~ ConsumerOf[qw/Role1/] # -> ""

=head2 BoolLike

Tests for 1, 0, "", undef, or an object with an overloaded C<bool> or C<0+> operator as C<JSON::PP::Boolean>. In the second case, it calls the C<0+> operator and checks the result as C<Bool>.

C<BoolLike> calls the C<0+> operator and checks the result.

	package BoolLikeExample {
		use overload '0+' => sub { ${$_[0]} };
	}
	
	bless(\(my $x = 1 ), 'BoolLikeExample') ~~ BoolLike # -> 1
	bless(\(my $x = 11), 'BoolLikeExample') ~~ BoolLike # -> ""
	
	1 ~~ BoolLike     # -> 1
	0 ~~ BoolLike     # -> 1
	"" ~~ BoolLike    # -> 1
	undef ~~ BoolLike # -> 1
	
	package BoolLike2Example {
		use overload 'bool' => sub { ${$_[0]} };
	}
	
	bless(\(my $x = 1 ), 'BoolLike2Example') ~~ BoolLike # -> 1
	bless(\(my $x = 11), 'BoolLike2Example') ~~ BoolLike # -> 1

=head2 StrLike

A string or object overloaded with the C<""> operator.

	"" ~~ StrLike # -> 1
	
	package StrLikeExample {
		use overload '""' => sub { "abc" };
	}
	
	bless({}, "StrLikeExample") ~~ StrLike # -> 1
	
	{} ~~ StrLike # -> ""

=head2 RegexpLike

A regular expression or object with an overload of the C<qr> operator.

	ref(qr//)  # => Regexp
	Scalar::Util::reftype(qr//) # => REGEXP
	
	my $regex = bless qr//, "A";
	Scalar::Util::reftype($regex) # => REGEXP
	
	$regex ~~ RegexpLike # -> 1
	qr// ~~ RegexpLike   # -> 1
	"" ~~ RegexpLike     # -> ""
	
	package RegexpLikeExample {
	 use overload 'qr' => sub { qr/abc/ };
	}
	
	"RegexpLikeExample" ~~ RegexpLike # -> ""
	bless({}, "RegexpLikeExample") ~~ RegexpLike # -> 1

=head2 CodeLike

A subroutine or object with an overload of the C<&{}> operator.

	sub {} ~~ CodeLike     # -> 1
	\&CodeLike ~~ CodeLike # -> 1
	{} ~~ CodeLike         # -> ""

=head2 ArrayLike`[A]

Arrays or objects with an overloaded operator or C<@{}>.

	{} ~~ ArrayLike      # -> ""
	{} ~~ ArrayLike[Int] # -> ""
	
	[] ~~ ArrayLike # -> 1
	
	package ArrayLikeExample {
		use overload '@{}' => sub {
			shift->{array} //= []
		};
	}
	
	my $x = bless {}, 'ArrayLikeExample';
	$x->[1] = 12;
	$x->{array} # --> [undef, 12]
	
	$x ~~ ArrayLike # -> 1
	
	$x ~~ ArrayLike[Int] # -> ""
	
	$x->[0] = 13;
	$x ~~ ArrayLike[Int] # -> 1

=head2 HashLike`[A]

Hashes or objects with the C<%{}> operator overloaded.

	{} ~~ HashLike  # -> 1
	[] ~~ HashLike  # -> ""
	[] ~~ HashLike[Int] # -> ""
	
	package HashLikeExample {
		use overload '%{}' => sub {
			shift->[0] //= {}
		};
	}
	
	my $x = bless [], 'HashLikeExample';
	$x->{key} = 12.3;
	$x->[0]  # --> {key => 12.3}
	
	$x ~~ HashLike      # -> 1
	$x ~~ HashLike[Int] # -> ""
	$x ~~ HashLike[Num] # -> 1

=head1 Coerces

=head2 Join[R] as Str

String type with conversion of arrays to a string through a delimiter.

	Join([' '])->coerce([qw/a b c/]) # => a b c
	
	package JoinExample { use Aion;
		has s => (isa => Join[', '], coerce => 1);
	}
	
	JoinExample->new(s => [qw/a b c/])->s # => a, b, c
	
	JoinExample->new(s => 'string')->s # => string

=head2 Split[S] as ArrayRef

	Split([' '])->coerce('a b c') # --> [qw/a b c/]
	
	package SplitExample { use Aion;
		has s => (isa => Split[qr/\s*,\s*/], coerce => 1);
	}
	
	SplitExample->new(s => 'a, b, c')->s # --> [qw/a b c/]

=head1 AUTHOR

Yaroslav O. Kosmina LL<mailto:dart@cpan.org>

=head1 LICENSE

âš– B<GPLv3>

=head1 COPYRIGHT

The Aion::Types module is copyright © 2023 Yaroslav O. Kosmina. Rusland. All rights reserved.



( run in 1.124 second using v1.01-cache-2.11-cpan-6aa56a78535 )