I am trying to understand how lexical_has
attributes work in Moops. This feature comes from Lexical::Accessor
and, as I understand it, the lexical_has
function is able to generate a CODE
reference to any attribute a class
might "lexically have" by using a scalar reference (which is kept in accessor =>
). The CODE reference can then be used to access the class attribute in a way that "enforces" scope (because they are "inside out"??). But this is just my surmise and wild guesses so I would appreciate a better explanation. I also want to know why this approach doesn't seem to work in the following example:
Working from the example that is part of the Moops introduction I'm creating a class Car
:
use Moops;
class Car {
lexical_has max_speed => (
is => 'rw',
isa => Int,
default => 90,
accessor => \(my $max_speed),
lazy => 1,
);
has fuel => (
is => 'rw',
isa => Int,
);
has speed => (
is => 'rw',
isa => Int,
trigger => method ($new, $old?) {
confess "Cannot travel at a speed of $new; too fast"
if $new > $self->$max_speed;
},
);
method get_top_speed() {
return $self->$max_speed;
}
}
Then I instantiate the object and try to use its methods to access its attributes:
my $solarcharged = Car->new ;
# This correctly won't compile due to $max_speed scoping:
# say $solarcharged->$max_speed;
# This shows expected error "too fast"
$solarcharged->speed(140);
# This prints nothing - wrong behavior?
say $solarcharged->get_top_speed();
The last line which uses the custom accessor baffles me: nothing happens. Am I missing an attribute or setting for the class (marking it eager or lazy => 0
doesn't work)? Do I need a BUILD
function? Is there an initialization step I'm missing?
N.B. If I add a setter method to the class that looks like this:
method set_top_speed (Int $num) {
$self->$max_speed($num);
}
and then call it in my final series of statements:
# shows expected error "too fast"
$solarcharged->speed(140);
$solarcharged->set_top_speed(100);
# prints 100
say $solarcharged->get_top_speed();
the get_top_speed()
method starts to return properly. Is this expected? If so, how does the default from the class settings work?
I've reported this as a bug here: https://rt.cpan.org/Public/Bug/Display.html?id=101024.
Since One can easily work around this by using "perl convention" (i.e. not using lexical_has
and prefixing private attributes with "_
") and this question arose from a bug, I don't expect a fix or a patch as an answer. For the bounty - I would appreciate an explanation of how Lexical::Accessor
is supposed to work; how it "enforces" private internal scope on accessors; and maybe some CS theory on why that is a good thing.
package A { use Moo; use Lexical::Accessor; lexical_has attr => (accessor => \my $attr, default => sub { 90 }); sub get { shift()->$attr } }; say A->new->get() // "undef"
(printsundef
instead of the default value). This means it does not depend onMoops
. This seems to be an actual bug; have you reported it? – Stealing