Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re: Psychic Disconnect and Object Systems

by stvn (Monsignor)
on Apr 17, 2011 at 13:30 UTC ( [id://899810]=note: print w/replies, xml ) Need Help??


in reply to Psychic Disconnect and Object Systems

As the original designer of Moose (actually I stole pretty much all of the good stuff from other languages), I feel I should respond.

First, the new method normally takes parameters that match all my attributes.

Actually, this is just the default, you can adjust this in a number of ways. First there is BUILDARGS which allows you to pre-mangle the @_ parameters that are sent to the actual underlying constructor mechanism, using this mechanism you can create just about any interface you want for new. But it is also possible to just change the names of the parameters that are accepted by new using the init_arg option in the attribute definition. Here is a simple and contrived example ..

has 'secret_name' => ( init_arg => 'public_name', ... );
It is also possible to disallow the assignment of an attribute value through new altogether by doing the following (yeah this is an ugly use of undef here I know).
has 'secret_name' => ( init_arg => undef, ... );
In short, by default Moose provides a simple parameter to attribute mapping for new, but this is just the default and it is very easy to change this to create the API you desire.

Instead, my constructor arguments would be options used to configure the instance. Sure, some of these may be stored directly in attributes to be fetched later. But in general that's not the case. They will be used by the setup code to construct and arrange the various internal machinery of this instance, and may not be needed again ever.

Most of this is addressed above, but I wanted to address the final sentence in more detail. This is also possible using the BUILD method, since it gets the same set of HASH ref parameters that new gets and can then be used to construct the instance as you see fit.

package My::Object; use Moose; has foo => ( is => 'rw' ); sub BUILD { my ($self, $params) = @_; $self->foo( $params->{bar} ); }
You can think of BUILD as being more like a constructor in Java. In Java, new is actually a keyword and the JVM itself takes care of actually constructing the instance, then the constructor that is called is really just a "initializer" in which you can setup the instance.

Now I see there are ways to massage the arguments and such, but the framers of the system decided this was the normal thing to do, and the whole idea makes no sense to me. So am I fighting against it because I'm thinking about it wrong? What am I missing?

Much of this feature was actually borrowed completely from Perl 6 and I think that the idea behind the design was really one of the main ideas behind Perl. The idea that you should be able to whip up something simple and useful very quickly, but that you should also be able to grow that into something more complex and well thought out. By making this the default behavior and providing a number of ways to customize this (TIMTOWTDI) I think that Moose is simple just being "Perlish".

Also, you are not wrong. However I think maybe you are looking at things in a very C++ centric way and it may take some time for you to loosen the restraints on your mind. Perl is a very permissive language, in fact, of all the mainstream languages out there i would say it is the most permissive. This is a good and a bad thing, and while Moose does try and impose some consistency and structure to OO Perl, it also tries hard to stay Perlish and keep that open and permissive core that is honestly why I love Perl so much.

Second, what is the point of a read-only attribute? If it can't be set, it will never exist. There must be some "but..." in there.

No, there is no "but". A read-only attribute can only be set through the constructor, after that it cannot be set again. I find this very useful myself, but I suspect you might not since you seem less inclined to expose your attributes in your public API. I think that this again is your C++ maybe showing through, attributes in Moose are not the same as member variables in C++. A Moose attribute is really just a set of behaviors wrapped around the state, it is not the state itself.

Perhaps the best example of this comes when using the NativeAttribute features. For instance here is a simple Queue implementation using the Native Attribute traits that actually has no accessors at all.

package Queue; use Moose; has '_items' => ( traits => [ 'Array' ], is => 'bare', init_arg => 'items', isa => 'ArrayRef', lazy => 1, default => sub { [] }, handles => { 'enqueue' => 'shift', 'dequeue' => 'pop' } );
As you can see this exposes no attributes to the public API (is => 'bare' means do not generate any accessors). It allows you to pass in the initial set of items in the queue through the constructor arg items, but it provides no direct access to them afterwards. If nothing is passed in, it uses lazy/default to create an empty ARRAY ref for you. It then uses delegation and the Native Attributes features to create an enqueue and dequeue method which perform the shift and pop operations on the ARRAY ref.

So as you can see, simple attributes can just serve to expose a member variable, but with a little more complexity and code you can actually create behaviors rather then just state accessors.

More generally, there is no distinction (in the docs) between internal access by the instance, and access by others using the object. So how do I make attributes that are for internal use?

Well, we encourage you to always use methods to access state, both internally and externally. This is not a Moose thing really, this is Perl OOP actually, Moose just makes it easier.

Just giving it a name starting with an underscore is a collision waiting to happen, since the accessors are virtual methods, and my slots should be local to this specific class even if a derived class declares a slot with a name that happens to be the same.

Honestly, I don't think I have ever really had a name collision on a private attribute, I think while it is a possibility, it will be waiting a long time to happen. If you are concerned about this though you can certainly name your private attributes using the class name as a prefix or something, this would eliminate the possibility of collision, but really is overkill. My suggestion really is to not worry about it because it is unlikely to happen, and when it does, you can address it. Again though, this is not Moose, this is Perl OOP (and Python and Ruby and any other dynamic language which does not have true private methods).

Is there a separate slot-access syntax that's not in the tutorials and manual? And why isn't that super prominent instead?!

There is, but it involves going through the meta-layer and it is really ugly and verbose. Again, you worry too much, sit back and relax, this is Perl.

My native language is C++, and I have looked at the Perl 6 object system and it all makes good sense to me. So what am I missing?

Like I said before, I think you are maybe just looking at this with your C++ glasses on. Every programming language has a set of qualities that encourage you to think and write code in a certain (idiomatic) way, some of these things transfer from language to language, but not all. I encourage you to take some time and play around with Moose as Moose and to put aside (at least for the moment) some of your C++ reactions, you might like it.

... or you might not, which is also perfectly fine, Moose is not for everyone (see also BrowserUK).

-stvn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://899810]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2024-04-19 02:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found