p5-mop-redux
p5-mop-redux copied to clipboard
Attribute of static class means the default value for this attribute
I've changed the behavior of generated accessors for static class. I think the best way is to read or change the default value of an attribute.
This is the same behavior as for Python classes:
class A:
a = 1
b = 2
class B(A):
b = 3
print A.a # 1
print A.b # 2
print B.a # 1
print B.b # 3
A.a = 11
B.b = 33
print A.a # 11
print B.a # 11
print B.b # 33
b = B()
print b.a # 11
print b.b # 33
b.a = 111
print b.a # 111
print B.a # 11
The same example coded with mop and this patch:
use v5.14;
use mop;
class A {
has $!a is rw = 1;
has $!b is rw = 2;
}
class B extends A {
has $!b is rw = 3;
}
say A->a; # 1
say A->b; # 2
say B->a; # 1
say B->b; # 3
A->a(11);
B->b(33);
say A->a; # 11
say B->a; # 11
say B->b; # 33
my $b = B->new();
say $b->a; # 11
say $b->b; # 33
$b->a(111);
say $b->a; # 111
say B->a; # 11
Hmm, I am not sure about this, I generally dislike this kind of dual-purpose behavior. The rw accessor is an instance method, and you are using it as a class method that affects the underlying metaclass.
It is also entirely possible to do this by hand via the mop, so adding a short cut like this is not really necessary.
This would also cause trouble with non-trivial defaults (like has $!foo = $_->build_foo
or whatever). I also don't like this much.
Also, this would be quite easy to do as an extension if you really wanted this behavior - I just don't think it should be the default.
I did also a proper trait:
use v5.14;
use mop;
use Scalar::Util qw(blessed weaken);
sub static {
my $attr = shift;
if ($attr->isa('mop::attribute')) {
weaken(my $weak_attr = $attr);
$attr->bind('after:FETCH_DATA' => sub {
my (undef, $instance, $data) = @_;
if (not blessed $instance) {
$$data = $weak_attr->get_default;
}
});
$attr->bind('after:STORE_DATA' => sub {
my (undef, $instance, $data) = @_;
if (not blessed $instance) {
$weak_attr->set_default($$data);
}
});
} elsif ($meta->isa('mop::class')) {
mop::traits::util::apply_trait(\&static, $_) foreach $meta->attributes;
}
}
class A is static {
has $!a is rw = 1;
has $!b is rw = 2;
}
class B extends A is static {
has $!b is rw = 3;
}
say A->a; # 1
say B->a; # 1
A->a(11);
say A->a; # 11
say B->a; # 11
B->a(111);
say A->a; # 111
say B->a; # 111
say A->b; # 2
say B->b; # 3
B->b(33);
say A->b; # 2
say B->b; # 33
my $b = B->new;
say $b->a; # 111
say $b->b; # 33
$b->b(333);
say $b->a; # 111
say $b->b; # 333
But still I think this could be the default behavior. Now calling CLASS->accessor causes unspecified effect. It should be more intuitive. I think Python has a really nice sugar for changing the default value of the attribute.