I'm about to choose what language to use for a new project: Perl5 or Perl6. 6 wins so far except that it is missing Moo
's lazy attributes. The two implementations I found in modules are missing the key functionality. Hence, my attempt write my own implementation.
First problem I've got into is the content of attribute's .package
for one declared in a role. Consider the followin:
role HOW1 {
method compose ( Mu $class ) {
note "HOW1.compose";
nextsame;
}
}
role HOW2 {
method compose ( Mu $class ) {
note "HOW2.compose";
nextsame;
}
}
multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
note "Attribute's package.HOW: ", $attr.package.HOW;
note '$*PACKAGE.HOW: ', $*PACKAGE.HOW;
$attr.package.HOW does HOW1;
$*PACKAGE.HOW does HOW2;
}
class Foo {
has $.bar is mooish;
}
role FooRole {
has $.baz is mooish;
}
The output of the script follows:
Attribute's package.HOW: Perl6::Metamodel::ClassHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ClassHOW.new
HOW2.compose
HOW1.compose
Attribute's package.HOW: Perl6::Metamodel::GenericHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ParametricRoleHOW.new
HOW2.compose
As it is clearly seen from the output, applying a role to a metaclass always works for classes and only works for $*PACKAGE.HOW
with roles. Use of $*PACKAGE
instead of .package
could be considered a solution, but not the one I'd really like to use. (Though, if there is no better way...)
I would like to provide lazy functionality for private attributes too. Yes, this will be availabe with self!bar
syntax only, but this is a sacrifice I'm willing to make. 😉 The problem is that all the examples of custome-made accessor I found so far are using Attribute.set_value()
method which is way too low-level. I'd like to have something like this:
role MooishHOW {
method compose ( Mu $class ) {
my $accessor = $class.^add_private_method( 'bar1',
method () is rw {
note self.WHO, ".bar1";
Proxy.new(
FETCH => -> $o {
$!bar1;
},
STORE => method ( $val ) {
note "Storing";
$!bar1 = $val;
}
);
}
);
callsame;
}
}
multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
$attr.package.HOW does MooishHOW unless $attr.package.HOW ~~ MooishHOW;
}
class Foo {
has $.bar is mooish;
has $!bar1 is mooish;
method to-bar1 {
note "bar1 val:",self!bar1;
}
}
my $inst = Foo.new;
$inst.to-bar1;
But $!bar1
notation doesn't compile because of the scope (MooishRole
). Are there a trick I'm missing which would allow referencing a private attribute on self
?
Perhaps it is possible to make an attribute to be a Proxy
container? This would greatly simplify the overall logic of laziness implementation.
I have answered all my questions by finally achieving the target and released AttrX::Mooish module.
So far, the answer for the first question is: no. $*PACKAGE
is currently the only way.
Second question: have no answer, but the final code has to rely on set_value()
anyway.
The tricky one happened to be possible: set_value()
does binding of an attribue to a container making it possible to bind to a Proxy
object. No need to for sacrifices, private attributes can be accessed directly with lazyness working on them.
Thanks everybody, your answers let me work around some rough edges!