2 posts tagged “moosex-dependent”
So in my attempt to add in some support for Dependent Type programming to Perl Moose programming I'm at the part where I need to integrate this into declaring your Moose based Perl objects. To recap, with a Dependent Type Constraint you can declare a constraint like:
subtype Range,
as Dict[max=>Int, min=>Int],
where {
my $range = shift @_;
return $range->{max} > $range->{min};
};
subtype RangedInt,
as Dependent[Int, Range],
where {
my ($int,$range) = @_;
return ($int >= $range->{min} && $int <= $range->{max});
};
RangedInt([max=>10, min=>1])->check(5); ## Is fine
RangedInt([max=>10, min=>1])->check(15); ## Is NOT fine
You can then use this in a Perl class like so:
class MyClass {
has young_adult_age => ( is=>'ro', isa=>RangedInt[min=>18,max=>34] );
}
And you can instantiate this like so:
MyClass->new(young_adult_age=>20);
But the following will throw an exception:
MyClass->new(young_adult_age=>40);
Also, if you are using MooseX::Declare you can use these in your method body signatures:
method height(RangedInt[min=>10,max=>100] $height) { ## Do something here }
All this is supported in the current repository version you can review here.
So far so good. However, it's been requested to be able to fulfill a dependency requirement via $self when using dependent types in a class. My proposed syntax for this is to make the 'isa' and 'does' attribute options similar to default, where if the attribute is lazy and the value is a coderef, we de-reference it and pass $self. So, for example, let's say you create some type constraints and a class that uses it like so:
use Set::Scalar;
use Email::Valid;
subtype Email,
as Str,
where {
return Email::Valid->address($_);
};
subtype Set,
as Dependent[class_type 'Set::Scalar', Any],
where {
my ($set, $item) = @_;
foreach my $element ($set->members) {
return unless $item->check($element);
} 1
}
subtype UniqueEmail,
as Dependent[Email, Set],
where {
my ($email, $set) = @_;
return !$set->has($email);
}
class Person {
has email_set => (is=>'ro', isa=>Set[Email] );
has email => (is=>'ro', isa=>UniqueEmail[Set[Email]] );
}
Ideally, when you try to instantiate this class, the attribute 'email' would be bound to attribute 'email_set', in such a way that it's value should be unique in that set. To achieve this, I am proposing an attribute trait that allows something like:
class Person {
has email_set => (is=>'ro', isa=>Set[Email] );
has email => (
is=>'ro',
isa=>sub {
my $self = shift @_;
my $set = $self->email_set;
return UniqueEmail[$set];
},
traits=>['Dependent'],
);
}
And at validate time, the coderef would be dereferenced with $self, similarly to the way the default option works. We'd do the same for the 'does' attribute. And to make it even more similar to default, I'd support coderefs here without lazy, which would not pass $self, but would execute at runtime.
For myself, I'd rather have another solution, but maybe I just don't fully grasp the use cases. Comments on the above proposals very welcome. However, if you are wanting something very different, you should give me enough specification for me to get it.
==UPDATES==
It's been suggested that instead of overloading 'does' and 'isa', to use a new options, such as 'generate_isa' or similar. Should be more sane and play nicer with other attributes that expect isa and does to behave a certain way.
One thing which has come up from time to time on the Moose Type Constraint wish list is the ability to more easily define type parameters. For example, it would be great if we could very simple create an Integer type that is bound by a range. For example:
subtype Range,
as Dict[max=>Int, min=>Int],
where {
my $range = shift @_;
return $range->{max} > $range->{min};
};
subtype RangedInt,
as Dependent[Int, Range],
where {
my ($int,$range) = @_;
return ($int >= $range->{min} && $int <= $range->{max});
};
class MyClass {
has young_adult_age => ( is=>'ro', isa=>RangedInt[min=>18,max=>34] );
}
And you can instantiate this like so:
MyClass->new(young_adult_age=>20);
But the following will throw an exception:
MyClass->new(young_adult_age=>40);
This gives your Moose based Perl programming a bit more flexibility!
Right now this is not on CPAN, but you can see it, play with it (and help me shed light on any of the corner cases) at: https://jules.scsys.co.uk/gitweb/gitweb.cgi?p=gitmo/MooseX-Dependent.git;a=tree
At this point the code is shaping up, although there's a bit of ugly stuff in the type constraint code. Even coercions work as you might hope.
Pending responses to the above syntax, remaining things before CPAN is primarily some attribute traits so that we can expose $self to a dependent type in a class. This would allow something like:
class Person {
has people => (is=>'ro', isa=>PeopleResultSet, required=>1);
has id => (is=>'ro', is=>UniqueID[PeopleResultSet], traits=>['Dependent']);
}
The idea being that the attribute 'id' would be bound to the attribute 'people' so that you could not create a new Person that had a pre-existing ID. The attribute trait stuff is still pending and feedback, test cases / use cases and of course code are very welcome.
UPDATED: My apologies for rebranding the date to anyone who has a working aggregator, but seems that whatever the settings for the Ironman aggregator, it wasn't picking up my post. Maybe I'm supposed to say, "Perl Programming now..."