http://qs321.pair.com?node_id=902415


in reply to Some thoughts on Moose Attributes

Basically, it is not nice to have to call functions to get and to set each time.

Perhaps, but this is the cost of encapsulation.

I noticed later that some use of "native traits" delegation can mitigate that, but not completely, as it would lead to a proliferation of specific methods;

Actually the best usage of the native traits feature does not expose all the features of the underlying data structure, but instead uses the delegation methods to craft an appropriate API that doesn't at all expose the underlying data structure. This way if you decide later on down the road that it is better to use Set::Object then a plain array, you only need to change a few internal methods and none of your classes consumers need to care.

If really what you want to do is expose all the delegate-able methods of the data structure, I recommend looking at something like Moose::Autobox. Though to be honest, I really dislike Autobox for a number of reasons.

but that's an issue really because Perl built-in types like arrays are not themselves accessed via method syntax and built-in functions don't take references.

Again, if that is what you want, Moose::Autobox, but if you want the encapsulation/data-hiding/implementation-hiding that is one of the key concepts of OOP, then you want to use your native delegations carefully and focus on exposing the correct API, not the underlying data structure you implemented it with.

The real issue, that can't be put off on other things still needing to catch up, concern very simple types like plain numbers and strings.
....snip...
Or perhaps$self->input_line_number(1+$self->input_line_number);. But, recall that the ++ syntax is beloved by millions and for a reason! It is quite a bit out of the way to not be able to manipulate things in-place.

Why not use the Moose::Meta::Attribute::Native::Trait::Counter native attribute? With an attribute like this

has 'input_line_number' => ( traits => [ 'Counter' ], is => 'ro', isa => 'Int', default => 0, handles => { 'inc_line_number' => 'inc', 'dec_line_number' => 'dec', '_reset_line_number' => 'reset', 'skip_ahead_two_lines' => [ 'inc' => 2 ] } );
You would have single method calls to increment and decrement your line number, which underneath are inlined subroutines which do direct access to the underlying hash (avoiding that 2 method call overhead). Also, as you can see I added a private reset method as well, and another method which uses the native attribute "currying" feature to create a method that will increment by 2 every time.

Remember, this is Perl, there is always more than one way to do it, and Moose tries very very hard to be as Perl-ish as possible.

So here is a specific question: why not have an lvalue accessor in Moose?

Because lvalue subs are poorly implemented in Perl, they do not in any way allow for hooks to be added in and they essentially expose the underlying data object as well as its type, which pretty much breaks encapsulation (if I know I can ++ on the lvalue sub, then I know that the underlying value *must* be a number, if I want to change that implementation detail, too bad, my class consumers will break). Sure you can work around this with some awful tie() hacks and perhaps even using Variable::Magic and I am pretty sure people have actually done this, however the code was obviously too scary/ugly to release as an actual module.

Now, don't get me wrong, I actually love the idea of lvalues. When I code in C# (which I have to do every once in a while), I really enjoy a number of the language features it provides, one of which is Properties, which is (IMO anyway) lvalues done right.

It's always been large in Perl 5, but at least I could refer to the slots (via hash access) as lvalues. It seems to be a chore, all the time, for code to get to its values. In C++ you just say x in scope; in Moose you have to say $self->x twice, to get and again to set, and work on a copy.

I know, wouldn't it be awesome if we had proper classes? Which had proper scoping (per-instance, not just block-scoping)? I would absolutely love to see that come about some day, and on that day I will almost certainly stop using Moose and start feverishly porting over any and all useful MooseX:: modules to work with the new and beautiful world of real, proper classes in Perl (notice i didn't say OO, but classes, there is a big difference).

But until then we are still stuck in this ghetto of Perl back-compat and DIY-OOP-1990-isms. You see, this is exactly why I wrote Moose in the first place. I had spent over a year working on the Pugs project, prototyping MOP after MOP and tasting the awesomeness that was to be Perl 6 OO, only to have to go back to vanilla Perl 5 OO for $work code. Eventually I decided I needed a way to do the great stuff I could do in Perl 6, in Perl 5, and I needed it to be stable and sane enough in the Perl 5 world that I could use it in production.

Moose is not the ideal OO system, in fact, it is very very far from it. A truly great OO system would not have to exist as a library atop the language itself, it would not have to deal with some of the really fugly crap that we have to deal with in order to make Moose work and just as importantly, make Moose play well with non-Moose code (after all, what is Perl without the CPAN, dead I tell you, dead! (just kidding, but you get the idea)).

In the past I have referred to Moose as a disruptive technology and underlying this reference is my hope that someday, like any good disruptive technology that gains enough momentum, it will get replaced by an even better version of itself. Hopefully Moose will help serve as a stepping stone to the next level, be that Perl 6 or just a really far off version of Perl 5, pretty much as long as it is Perl I will be happy.

Anyway, enough preaching for a sunday, time to go watch some TV with the wife and sip a nice beer.

-stvn

Replies are listed 'Best First'.
Re^2: Some thoughts on Moose Attributes
by John M. Dlugosz (Monsignor) on May 02, 2011 at 03:39 UTC
    Because lvalue subs are poorly implemented in Perl, they do not in any way allow for hooks to be added in and they essentially expose the underlying data object as well as its type, which pretty much breaks encapsulation (if I know I can ++ on the lvalue sub, then I know that the underlying value *must* be a number, if I want to change that implementation detail, too bad, my class consumers will break).
    In C++, if I refer to a field, I can't readily make that get/set or in-place modification do something else. It doesn't have "properties" ala C# and other Microsoft languages. Accessing a data member is syntactically distinct from calling a function in every way, since functions must have parens even if there are no parameters.

    So, I'm used to that. I refer to data members directly only when they are private implementation detail, and use accessors for public API.

    It is mitigated somewhat because names are checked at compile time, so if I had to upgrade a bare field to something hiding behind an accessor, I could remove the field and be sure that all uses were noticed before I ran anything.

    Basically you make the same point as browserUK. The reason lvalue accessors are not used is because it doesn't hook setting to perform type checks, coersions, and whatever other hooks.

    You covered a lot of ground in your post, so I'll reply to different parts separately or perhaps even start new threads.