Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Ovid (2709)

Ovid
  (email not shown publicly)
http://publius-ovidius.livejournal.com/
AOL IM: ovidperl (Add Buddy, Send Message)

Stuff with the Perl Foundation. A couple of patches in the Perl core. A few CPAN modules. That about sums it up.

Journal of Ovid (2709)

Wednesday June 03, 2009
09:57 AM

Spot the Bug!

[ #39070 ]

Today I spent a couple of hours tracking this bug down. The bug is mine, but relies on what I think is rather counter-intuitive behavior in Moose (due to how Class::MOP works). I trawled through a lot of source code before I could replicate a simple test case. I'm not sure why Moose behaves this way and from what I can tell, it's not documented in Moose, but implied via the Class::MOP::Attribute documentation.

#!/usr/bin/env perl -l

{
    package My::Base;
    use Moose;
    has some_method => ( default => 'foo' );
}
{
    package My::Class;
    use Moose;
    extends 'My::Base';
}
print My::Class->new->some_method;

If you don't know Moose, you won't see the bug. In fact, I'd wager that many who are comfortable with Moose won't see the bug right away, if at all. You'll see the problem if you run the code (I'm on the latest version of Moose), but the fix won't be apparent unless you understand why the bug occurs.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • I definitely don't know Moose well enough, and I fail to see why it doesn't work.

    Care to provide some explanation?

    • The attribute is not declared rw/ro or given a reader, so no method is created.

      --
      rjbs
      • Well, OK, but then the example provided is not minimal - My::Class is actually not relevant. The problem would display itself also if it was called directly on My::Base.

        • And it does display itself if called directly on My::Base. I represented it as above because that's how it manifested in our code.

  • ...did you call it 'some_method'. has is not sub. It's a nice red herring though, throwing me off completely till I saw rjbs's answer. If it'd been called "some_field", it'd been obvious.

    rjbs has explained what went wrong, but I'm curious why you'd expect it to behave differently. The behaviour is exactly as documented in Moose's section on is => 'rw'|'ro'.

    • Sorry about that. I just hastily threw together an example. But what's the value of allowing me to declare an attribute without an accessor or mutator? I'm sure there's a reason. I just don't see it.

      • The slot is still accessible via the MOP, which means other accessor-like methods can access it. For example, with MooseX-AttributeHelpers you could have an ArrayRef container providing push and pop methods without providing an API to directly set the array reference value.

        --
        Ordinary morality is for ordinary people. -- Aleister Crowley
        • It would be nice to have to explicitly request that, though? Instead of it being the silent default. Or else there might be has_ro and has_rw keywords that one could use for the bulk of one’s declarations, in which case it’d be immediately obvious when something uncommon is going on.

          • You could do this with an attribute trait, but putting "is=>'ro'" is not particularly onerous and it reinforces the idea that an attribute is not just a fancy getter/setter sugar code, but is fundementally more powerful. At this point I doubt this is going to change in Moose, although MooseX::Declare might be open to new ideas (not sure what the authors of that have in mind over the long term.).
            --
            Waiting on the Road to Eventually, I lost my Place On Line
            • At this point I doubt this is going to change in Moose, although MooseX::Declare might be open to new ideas.

              MooseX::Declare is part of the overall Moose project, it will almost certainly not deviate too far from what Moose does on key issues like this. However which might be nice is to add support for the Perl-6 style of doing this, like so:

              class Foo is rw {
                 has bar;
                 has baz;
              }

              Here all of Foos attributes will be (is => rw) automagically. I think the key issue here is that Moose will not and should not do anything you don't ask it to do. DWIMery is very VERY slippery slope, too much o

              • My only suggestion is that the default be (is => 'ro') for reasons that I think are probably obvious to you. That being said, I accept an 'rw' default as most OO programmers are going to expect that an not agree that 'ro' is so important.

                • class Foo is ro {
                        has bar;
                        has baz;
                  }

                  # or in Plain Moose

                  {
                  package Foo;
                  use Moose -traits => ['ReadOnly'];
                  use namespace::autoclean;

                  has bar;
                  has baz;

                  __PACKAGE__->meta->make_immutable;
                  }

      • There are a few reasons I can imagine, and several I'm fairly sure I'm too afraid to consider. :-)

        One reason would be because one wants to explicitly set the 'reader' and the 'writer'.

        has babelfish => (
            writer => 'schrieb_fish',
            reader => 'lit_fish',
        );

        But that doesn't explain why there is no warning if there is no reader/writer assigned at all. But that could happen because one want to create those sometime during runtime. Wacky, but I can imagine a few motivations to

        • role Pet::Sounds {
              method make_noise { say $self->noise }
          }

          class MyHouse {
              has pet => (
                  does => 'Pet::Sounds',
                  required => 1,
                  handles =>  'Pet::Sounds',
              );
          }
          MyHouse->new(pet => Beetle->new())->make_noise; # I want pet provided on creation but never accessed directly after that.
        • The answer to "why" is simply "because you didn't ask for it". DWIMery is good up until it gets in your way, then it's annoying and difficult to work around. Here is another example (along with perigrin's excellent one) on why you might not want an accessor.

          has foo => ( trigger => sub { my ($self, $value) = @_; $self->bar( $value + 2 ) } );
          has bar => ( is => 'rw', ... );

          Here the side-effectual trigger is used to set the value of bar. Sure, seems kind of esoteric, but it is a valid use

  • Ovid,

    Your question was answered on the mailing list [perl.org], it is not a bug and this issue has been discussed many times before over the last 3 years and we always come back to the same conclusion that a default for is is too much DWIMery.

    - Stevan

    • Thanks Stevan. No worries. I personally accept some DWIMery for what I envision as the common use case and accept that this sometimes make the syntax a bit more difficult for other cases, but at the end of the day, it's no big deal. Now that I understand this, I can deal with it, though a better error message might help (though at the cost of tracking this information, I've no idea if the net impact for this use case is beneficial).

      My apologies for raising an issue that's been raised before. I googled q

      • I personally accept some DWIMery for what I envision as the common use case and accept that this sometimes make the syntax a bit more difficult for other cases, but at the end of the day, it's no big deal.

        I understand your issue, but the number of possible combinations available for has is huge and when you start introducing default values for any of them you are heading down a very slippery slope. As you said in the comment above, you would prefer ro, but some people would expect rw, while still others would rather we support PBP-style accessors and then there is the semi-affordance crowd, and the public reader/private writer crowd, the list can go on and on. In my mind the only solution to all these diffe

        • The idea of a better error message would actually be to trap the error and see if there was an attribute up the tree which didn't have any accessor defined for it and warn on that. That being said, it seems like it might be too much work for too little benefit.