Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Re^15: $class = ref $class || $class (not me)

by tye (Sage)
on Mar 06, 2003 at 00:14 UTC ( [id://240755]=note: print w/replies, xml ) Need Help??


in reply to (Re:)+ $class = ref $class || $class
in thread Constructor/Factory Orthodoxy

It is not reasonable to attribute use of the offending idiom to "mindless imitation".
I [...] have admitted to repeatedly doing so myself. [...] you will see others confessing also. tye comes to mind offhand.
Sorry, wrong.

I've scanned all of the threads you linked to and have to assume that you have grossly misrepresented what I said in (tye)Re: You Say "Cargo Cult" like it's a BAD thing!:

I've come to hate the term "cargo cult" myself. Many people copied that constructor code from standard documentation. I thought such copying was more accurately called "best practices".
You'll note the conspicuous lack of "mindless" or any synonyms thereof. If I thought the copying in question was "mindless", would I have described it as "best practices"?

I copied that because I understood it and liked it. There was nothing mindless about it.

The most direct alternative is to have $obj->new() be interpretted as "$obj"->new() or something like     "Class::Next=HASH(0xdeadbeef)"->new() which is just ridiculous! I wonder how many of your classes have this problem.

A more reasonable alternative would be:

sub new { my $class= shift(@_); Carp::croak "Usage: ",__PACKAGE__,"->new( ... )" if ref $class; # ... }
but I haven't seen anyone advocating that yet. It is very Perlish to DWIM in such a case. In the face of the options available to handle this problem, I support the standard documentation presenting the simple DWIM work-around that leads to reasonable behavior in more cases. It is a very simple approach and it is more "robust" rather than "strict". IMO, it is the best choice (by far) for introductory writings on Perl OO.

In a much more advanced treatment of Perl OO, I'd expect much more discussion of the problems with that and the reasonable alternatives to it. I have covered two alternatives above (though I only consider one to be reasonable). If I didn't want to allow $obj->new() then I would separate my class methods from my object methods by putting the class methods in one package and having constructors create objects blessed into a different package that contains only the object methods. But that creates quite a few other complications, especially if you want to "support" willy-nilly inheritance which the beginning Perl OO documents appear to want you to do.

It is rather funny how much ire is directed at that one line. It brings out wails about 'cloning' and yet no cloning is done in that line. You should be complaining about some other line of code that actually copies data from the old object to the new.

And, since I've read and scanned lots of notes on this topic and don't recall seeing an example of what to replace that line with, I assume that you want me to avoid this type of example:

sub Foo::new { my $class= shift(@_); return bless [@_], ref($class)||$class; }
in favor of this:
sub Bar::new { my $class= shift(@_); return bless [@_], $class; }
so that my users can see this:
my $obj= Bar->new(1,2,3); my $other= $obj->new(4,5,6); $other->twirl(); Can't locate object method "twirl" via package "Bar=ARRAY(0xbadd0g +5)"
and have probably no clue what the heck went wrong.

There must be some examples of what alternative I should use somewhere in one of these long threads. Someone find one so we can see it. It is certainly rare that such gets shown.

But I choose to use the oft-hated idiom for more than just that reason. I find that when writing a Perl module for public consumption, there are usually options that I want to give to potential users of my module. I want to support my module being used in more than one place. I want to support each place that uses my module to select the options that make the most sense in the situation at hand. I expect the people who use my module to often want to deal with more than one object at once. I think modules should have unique names and so they tend to have rather long names. So I like to let the module user just mention the module name once (in a use statement) and get a customizable factory object put into a lexical variable with a name that suits their needs.

In fact, I had to go back and make Win32::TieRegistry->new() even work because I found that people expected that to work despite it never being mentioned in the documentation for that module.

So instead of having to write, over and over:

my $key= Win32::TieRegistry->new( "NameOfRegistryKeyToOpen", { Delimiter=>"/", ArrayValues=>1, # Lots of other options }, );
I let you just do:
my $key= $Registry->new( "NameOfRegistryKeyToOpen" );
Now, whether that is "clone" or "new" doesn't much matter to me. [ In fact, you can use $reg->open("key") to get the same thing. I prefer open() but support new() because of the expectations of Perl programmers. ] But the design of new()/open() that the key name is required so that it doesn't really make much sense as a "clone" method since it will be opening a new key. Sure, it copies the options from the object being used as a factory into the newly created object, but I would never name such an operation "clone" ("Here is my clone.", "Um, why doesn't he look just like you?"). You could call it "copy" but that isn't a great fit either.

I can see where you might not like "new" because you could read it as "shiny new, having no baggage from anywhere else". Well, I say get over that interpretation if you are doing Perl OO. You see all of the grief it is causing you. In my case, use open() instead. But if you think of "new" as meaning "create an object", then you can use that.

So, you see, in Perl OO, there is no distinction between class methods and object methods (they are both just subroutines in the same package) so I like to make my Perl objects have the ability to be both objects and factories. It is nice to be able to create a not-fully-initialized object for when it is convenient to set some aspect of the object before you set others. So it is only natural to be able to use those as factories as well.

Sure, in some other OO language I would probably have a separate factory class and would prevent you from creating partially initialized objects so that I wouldn't have to make each method check "is this one ready yet". That is often a very good idea. It is like strict.pm, being less convenient and more strict in hopes of catching mistakes earlier.

Note, there are things about the latest Win32::TieRegistry that I don't like. For example, exporting the factory object was a bad idea and I haven't quite worked around that early mistake as much as I'd like to yet.

But I very much like my ref($class)||$class and I copied it from the standard documentation after reading about it, understanding it, and consciously deciding to use it. I understand it even better now and like it even more.

And it is the best technique for new Perl OO programmers to use in order to prevent confusion. So I even like it when it is mindlessly copied (though I'd rather people not program mindlessly, but I don't always get to control that).

Some other alternatives (which I haven't noticed the haters-of-this-idiom putting forth) are better for more long-term, big-project, careful, strict coding projects. Such projects should have careful coding standards and not be mindlessly copying stuff out of beginner manuals. If this is a problem that you run into then I suggest you get some coding standards for your project rather than trying to enforce coding standards on the rest of the Perl world (since Perl is such a DWIM/convenience language and Perl OO just doesn't do "strict" very well, IMHO).

                - tye

Replies are listed 'Best First'.
(Re:)+ $class = ref $class || $class (not tye)
by rir (Vicar) on Mar 06, 2003 at 06:25 UTC
    My apologies to tye. I retract my reference to tye's post. I apologize for grossly misrepresenting your post, Tye. I was thinking of a post textually near yours. Normally I recheck such stuff but I was experiencing repeated server errors and local network problems as I mentioned in that post.

    I guess I'm unique in confessing: I without any real critical thought trusted what I read in the second Camel back in '96 or so. Not having any experience with OO combined with a typing system like Perl's I don't think my judgement should have been used to second guess Wall, Christiansen and Schwartz's on this.
    Since it worked and no one criticized it, I never gave it much thought until I saw one of merlyn's nodes here last fall. I did use some critical thought on some other matters in the intervening period.

    Incidentally, I am not fond of the term Cargo Cult. myself, I've used it in this thread to set some level or degree to my use of thoughtless and mindless imitation.

    If I thought the copying in question was "mindless", would I have described it as "best practices"?

    Apparently not. I'd say that following "best practices" has little to with critical thought; assessing and determining what are "best practices" does.

    The most direct alternative is to have $obj->new() be interpretted as "$obj"->new() or something like

    "Class::Next=HASH(0xdeadbeef)"->new()
    which is just ridiculous! I wonder how many of your classes have this problem.

    None. It is ridiculous, I don't see why you bring it up.

    ... more reasonable ...

    sub new { my $class= shift(@_); Carp::croak "Usage: ",__PACKAGE__,"->new( ... )" if ref $class; # ... }
    I advocate this usage. I did not think this needed spelling out. Your sample is almost exactly what I use, first I'd croak on a bad size of @_. If reworking my earlier code I might first use carp to find the callers in the tests.

    In a much more advanced treatment of Perl OO, I'd expect much more discussion of the problems with that and the reasonable alternatives to it.

    I guess we are doing the advanced treatment here and now. Glad to have you join in.

    If I didn't want to allow $obj->new() then I would separate my class methods from my object methods by putting the class methods in one package and having constructors create objects blessed into a different package that contains only the object methods.

    Why? What does this split gain you?

    But that creates quite a few other complications, especially if you want to "support" willy-nilly inheritance which the beginning Perl OO documents appear to want you to do.

    I agree about the complications. I'm not sure what willy-nilly inheritance is.

    It is rather funny how much ire is directed at that one line. It brings out wails about 'cloning' and yet no cloning is done in that line. You should be complaining about some other line of code that actually copies data from the old object to the new.

    I am not wailing, I was trying to elucidate a point, I don't consider it a major issue but small improvements that can be idiomatic have little cost. If you read this thread it should be clear that I am not criticizing the mingling of cloning and construction.

    Yes, I do see

    sub new { bless {}, ref $_[0] || $_[0]; }
    as worse than
    sub new { croak "new takes class" if ref $_[0]; bless {}, $_[0]; }

    Can't locate object method "twirl" via package "Bar=ARRAY(0xbadd0g +5)"
    and have probably no clue what the heck went wrong.

    This argument is disingenuous, you yourself have demonstrated that with simple error checking this is not necessary.

    There must be some examples of what alternative I should use somewhere in one of these long threads. Someone find one so we can see it. It is certainly rare that such gets shown.

    You've written it yourself here.

    ...I like to let the module user just mention the module name once (in a use statement) and get a customizable factory object put into a lexical variable ...

    That is nice. It's not relevant to your main point. Update: Everyone should consider the chance of name conflicts when doing this.

    Your use of $obj->new( "stuff") to clone or partially clone $obj, I see as very reasonable. With the implications in your example I find it extremely so. Like you I'd prefer a different name.

    ... in Perl OO, there is no distinction between class methods and object methods (they are both just subroutines in the same package)

    Yes, I said that earlier in this thread.

    so I like to make my Perl objects have the ability to be both objects and factories.

    This is certainly possible, but I don't see any causal relationship to your previous clause.

    being less convenient and more strict in hopes of catching mistakes earlier.

    If the hopes are realized then it is more convenient.

    ... it is the best technique for new Perl OO programmers to use in order to prevent confusion.

    Best? This is unsupported opinion. I would like to see your justification. It is good to know common idioms even if they are not optimal. But it would be better to learn better idioms also. If the finer nuances of better code are unnoticed by some, that doesn't make the code bad. Certainly (ref $obj)->new is not going to slow down a common Perler, and it presents more info than $obj->new in the simple case so I say it is better.

    I suggest you get some coding standards for your project rather than trying to enforce coding standards on the rest of the Perl world

    I posted my standards for comment. To say that I am trying to enforce coding standards on the rest of the Perl world is insulting, but I won't say you've gotten even yet.

    Your restraint and manners are admirable. I have the opinion that my faux pas really irked you and that you're writing below your usual level. So again: Sorry.

      Your restraint and manners are admirable. I have the opinion that my faux pas really irked you and that you're writing below your usual level. So again: Sorry.

      Read emotion into text-only on-line communication at your peril. (: My restraint was certainly lacking. I also use "you" when I mean "one" because I find that writing "one should" sounds pompous even though I hate not having a good distinction for that. Perhaps I just need to get over sounding pompous. So much of this could easily be taken personally (by you, rir) that wasn't actually directed at you.

      [ Come to think of it, I should also use "y'all" more. I actually like that word and it could even defuse some of the impression of pomposity (along with the spelling errors -- that I only occasionally make on purpose). Anyway, some of those "you"s should have been "y'all"s as well. ]

      So, sorry for my lack of restraint and thus lack of manners. (And thank you for the compliment.) It has more to do with limited time than ire at you or your remark. Wanting to deny the remark helped to motivate me, but much of the heat is directed at concepts not people.

      I'd like to clarify my position on a couple of (technical) points but now is not a good time for that. I just wanted to set the record straight that a simple apology for your fairly minor error is sufficient. I accept it and forgive you, of course.

      I'm sorry I didn't take the time to more carefully craft my reply. I completely understand one worrying that dire offense has been served when reading such a reply. It was hurried and sloppy and so contains misexpressions and also errors. I'll try to straighten some of that out more explicitly when I allocate more time for the endeavor.

      And thanks for your restraint and manners in responding. It is nice to take an (unintentional) step toward a flame fest and get a nudge backward rather than a shove forward. :)

                      - tye - The Pompous Texan
        It is better to address the reader than write about the abstract person. For a USAer I am formal and retiring yet I rarely use one when you will serve. I finding using one is not pompous, just unengaging. Here we are in a poor situation for the use of you, we might wish to address each other personally and address the readership at large. Confusion between the general and specific second person probably indicates some structural deficiencies in a post. Given the informality of the forum this is to be expected.

        The use of the reader is more suited to some styles fiction when one wishes to background the reader.

        I recommend against descending to dialect, y'all, as a supposed improvement. Dialect is usually more distracting than helpful. I am sympathetic to your intent. The parallels to my statements regarding $obj->new are striking.

        Your apology is accepted. Your post was very mild considering the provocation.

        I look forward to more of your thoughts on the matter.

        You mentioned programming guidelines earlier.

        The use of programming guidelines is an interesting tangent to the issue. It is a formal declaration of what issues are not to be thought about when creating code. Coding practices should be determined at the lowest possible organizational level for the smallest reasonable workgroup. This yields expediency while still allowing some chance of improving the quality of code through better ordinary feedback and cross pollination between groups.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (1)
As of 2024-04-18 23:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found