Moops lexical_has and default values
Asked Answered
J

1

11

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.

Johnajohnath answered 19/12, 2014 at 2:38 Comment(7)
The minimal test case seems to be 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" (prints undef instead of the default value). This means it does not depend on Moops. This seems to be an actual bug; have you reported it?Stealing
@Stealing thanks for the feedback. I'm about to add it to RT but I'm not sure if I should add it to Moops queue or somewhere else. I think your test case is perfect for inclusion wherever it goes ;-) Cheers.Johnajohnath
rt.cpan.org/Public/Bug/Display.html?id=101024Johnajohnath
I now have a reasonable answer that I will post but would like someone to get this bounty if possible.Johnajohnath
It is a bug. I believe it affects read-write accessors, but not readers. I haven't had a chance to figure out the cause right now, but hopefully in the next couple of days.Impermanent
PS: it may have been introduced in my recent refactorings of Lexical::Accessor. As a workaround, you could try downgrading to 0.003.Impermanent
a quick introduction to the concept of inside out attributes, though not in a moose context: perldoc.perl.org/perlobj.html#Inside-Out-objectsReproachful
F
1

According to the ticket filed by the OP, this bug was fixed in Lexical-Accessor 0.009.

Flavio answered 26/7, 2020 at 11:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.