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.

smonff
  • 3,399
  • 3
  • 36
  • 46
G. Cito
  • 6,210
  • 3
  • 29
  • 42
  • 2
    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? – amon Dec 19 '14 at 22:12
  • @amon 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. – G. Cito Dec 19 '14 at 22:40
  • 1
    https://rt.cpan.org/Public/Bug/Display.html?id=101024 – G. Cito Dec 20 '14 at 04:32
  • I now have a reasonable answer that I will post but would like someone to get this bounty if possible. – G. Cito Dec 26 '14 at 03:27
  • 2
    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. – tobyink Jan 03 '15 at 16:44
  • 2
    PS: it may have been introduced in my recent refactorings of Lexical::Accessor. As a workaround, you could try downgrading to 0.003. – tobyink Jan 03 '15 at 16:45
  • 1
    a quick introduction to the concept of inside out attributes, though not in a moose context: http://perldoc.perl.org/perlobj.html#Inside-Out-objects – ysth May 07 '15 at 07:50

1 Answers1

1

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

ikegami
  • 367,544
  • 15
  • 269
  • 518