Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Re: (Ovid - minor code nits) Re: Adding autoloaded methods to symbol table with using strict refs

by Anonymous Monk
on Feb 26, 2002 at 20:09 UTC ( [id://147699]=note: print w/replies, xml ) Need Help??


in reply to (Ovid - minor code nits) Re: Adding autoloaded methods to symbol table with using strict refs
in thread Adding autoloaded methods to symbol table with using strict refs

I started writing a text explaining why I like $class = ref $proto || $proto; but everything felt unnecessary to write, since everything seemed so obvious. So instead of explaining why I think it's proper and why I like it I'll flip the coin and ask why you think it's mostly inappropriate.

-Anomo

Replies are listed 'Best First'.
Mmmm ... cargo cult progamming is nummy!
by dragonchild (Archbishop) on Feb 26, 2002 at 20:28 UTC
    Actually, I don't think it's nummy, but that's neither here nor there.

    There is good reasoning behind doing my $class = ref $proto || $proto;. Unfortunately, 99% of people who do that have no idea why they're doing it. The smarter ones will realize that it's doing some sort of error-checking and chalk it up to that.

    What that's really doing is co-opting your constructor (typically called new()) and using it as a copy function, (typically called copy() or clone()). There is something in C++ (and, I believe, Java) called a copy constructor, which will do that cloning for you.

    I personally think that new() should be reserved for creating a new instance and clone() should be reserved for taking an existing instance and returning a copy of it. That is because the two functionalities are vastly different. new() almost never has to deal with recursive data structures, deep vs. shallow copying, and the like.

    In addition, EVERY single time I've seen ref $proto || $proto written, there was no provision made for a copy constructor. In fact, that was the last it did with $proto. Doesn't seem like much of a copy constructor to me!

    Personally, I prefer the following construction:

    sub new { my $class = shift; return undef if ref $class; .... }
    That makes it completely obvious that I'm using new() to return a new instance. I'll create a copier if (and only if) I need one.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      What that's really doing is co-opting your constructor (typically called new()) and using it as a copy function, (typically called copy() or clone()).

      I have rarely seen the equivalent of a copy constructor in someone's new method. Rather, I've seen the   my $class = ref $proto || $proto; trick used to add "make me a new one of these" semantics to the standard "make me a new instance of this package" semantics.

      If you're restricted to created instances of a named class, you have extra hoops to jump through to extend your software to accomodate new subclasses. By adding a "make me new one of these" capability, you defer past compile-time the decision of what package to create a new copy of.

        But that can be done by the caller, in a clearer fashion:
        my $new_thing = (ref $this_thing)->new(@params);
        This clearly says to me "for the class of $this_thing, call new". Very clear.

        You don't need to put (cargo-cult fashion) the "ref $proto || $proto" device in every single constructor. The caller can control this.

        If you want to provide a clone method in your class, do so. But don't try to make "new" do double duty. It confuses those of us with some common sense.

        -- Randal L. Schwartz, Perl hacker

      What that's really doing is co-opting your constructor (typically called new()) and using it as a copy function, (typically called copy() or clone()).
      In addition, EVERY single time I've seen ref $proto || $proto written, there was no provision made for a copy constructor.

      How do you manage to combine those two statements? Why is there a belief that new as an instance method is a clone method? As you say, it's hardly ever (never?) used as one so why do people think that it is? Is it just paranoia due to that one module once did that? Or that someone just invented that idea? Or is it an idea from some other language?

      I too have never seen new being used for anything else than a constructor. Or perhaps I have, but it must have been a long time ago if so. Neither have I ever even thought about incorporating a clone functionality into new. But I can understand the objections to using it if many people manage interpret $bar = $foo->new as $bar = $foo->clone.

      I also believe that this interpretation is not very probable if new has any arguments. Then you'd typically see that it's not a clone method. $other_dog = $dog->new($name) can hardly be confused as a clone operation, or can it?

      Just for fun I wrote a script that (tries to) find usages of $class = ref $proto || $proto or similar constructs. I ran it on all my installed Perl modules. I found quite a few. I rehacked the program to find usages of $obj->new; or similar. I found... none. Perhaps it's not terrific to make this search on these modules, since many of the installed modules doesn't use other objects, so let's not draw hasted conclusions. But perhaps it gives a pointer.

      I'm not sure I'll stop using $proto though. It's already a wide-spread style and some people like it. To stop using it would be to enforce a style upon another. It's not an either-or situation. Those that like to use $obj->new can continue if they feel like it. Those that prefer ref($obj)->new can do that as well. As for the confusion argument: if people get confused by seeing $class = ref $proto || $proto they're probably not too familiar with the Perl culture. If they get confused by $other_dog = $dog->new then I'd say the same: this is not common Perl practice and isn't to be expected. The "make a new object with the same class as this object" interpretation is the common interpretation.

      The one thing I might "enforce" or at least encourage through providing this usage is that the maintainer should have a fair share of knowledge about the common Perl practices. That I really do want to enforce. (Of course, this argument stands and falls on that new indeed never is being used a clone method -- something I have no reason to doubt in this moment.)

      -Anomo
        I'm not in favor of adding unused code to my program just because other people may be used to it. I'm also opposed to adding half of a feature, especially if it doesn't work. Why muddy the intent of the code for no benefit?

        Of all the people I've seen using this construct, only one could explain what it does. I'm leery of taking advice from people who don't know what they're doing or why it might be done.

        I'm also unrepentently preaching a style. "Write only what you need."

        As for the confusion argument: if people get confused by seeing my $class = ref $proto || $proto they're probably not too familiar with the Perl culture.

        The people complaining about this construction are familiar with "Perl culture." Heck, it's arguable that merlyn helped create some of that "Perl culture"! In my experience, the people who use this construction are the very people who are inexperienced with Perl culture. To call this part of Perl culture is to say that inefficient coding is part of Perl culture. That is something I will not accept.

        You also seemed to be confused by a programming-theory concept. A copy constructor is a constructor that is invoked as an instance method and uses the values of the invoking instance as the default values for the new instance. Any values passed in to the constructor override the values of the invoking instance. Hence, the line

        my $otherDog = $thisDog->new($someParameters);
        creates $otherDog using all the values of $thisDog, save when $someParameters would override those values. A clone() method would do the same thing.

        You also state that you found a large usage of my $class = ref $proto || $proto;, but didn't find any usage of those constructors as copy constructors. You then say that we shouldn't "draw hasted conclusions"sic. I personally think that this data does support a conclusion. If a line of code is a no-op in every instance, then it shouldn't be written. It slows down the code as well as confuses all maintainers of that code. To me, the latter is much more important than the former. Whenever I see my $class = ref $proto || $proto;, I'm looking for this constructor to have use as a copy constructor. When I don't see that, my opinion of the programmer's skill drops. The programmer obviously did not understand why s/he wrote that line.

        As to why that's important ... let's suppose that the line was really $class = ref $proto || $proto; (which doesn't have the my in front of $class). That raises an error under strict. If you didn't understand why the statement was there, you now have a broken module and the line that's breaking it is one you are afraid to fix! That's the problem with cargo-cult programming. It disperses code that is viewed akin to magic by those using it. "If I wave my hands and chant this verse, my code will work." I don't ever want to work with someone who's written code with that mentality. It just means that I have to clean up after them. I hate cleaning.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: Re: (Ovid - minor code nits) Re: Adding autoloaded methods to symbol table with using strict refs
by chromatic (Archbishop) on Feb 26, 2002 at 20:37 UTC
    That construct allows you to call new() on an already-constructed object, receiving back a new object. This would be handy in certain circumstances as a copy constructor.

    The problem is, there's no copying going on in the constructor. It creates a new, empty hash. That's not so useful.

    The only other reason something like this would be useful is if you want to create an object of the same type without knowing what that type is. I can't think of when that would be useful, and there are other ways to solve this, but I'd allow it in this case.

    Otherwise, it adds nothing. (Some people would argue that a copy constructor should have a different name, like "clone" or such. I lean toward the idea that methods should each do one thing well.)

(Ovid - ref $proto $proto) Re(3): Adding autoloaded methods to symbol table with using strict ref
by Ovid (Cardinal) on Feb 26, 2002 at 21:24 UTC

    One of the strengths/weaknesses of Perl's OO system is the lack of a clean separation of class and instance data and methods. This typically isn't that big of a deal, but many Perl programmers have never used OO programming with another language and may not appreciate the distinction between the two.

    Imagine that you are writing some sort of bank account software. In your specs, you see that if anyone has a minimum savings balance less than $100US, you stop paying interest on that account. Since this applies to all accounts, the limit of 100 dollars is class data. It doesn't apply to an individual account so much as it applies to all accounts. If you need to change the limit, you can adjust the class variable and all objects automatically pick up the new value (yes, this is a simplistic example). However, if you want to check my savings balance, you would check the instance value of my account's balance. Obviously, having my account balance be class data would not make sense.

    The separation between instance and class also affects methods. The typical new() constructor is a class method. In this case, it usually doesn't impart any new information to specific instances of the object being instantiated (it might do this, though, if you were keeping track of the number of accounts). It doesn't, however, typically belong to a particular instance of an object. That usually doesn't make a sensible model.

    The ref $proto || $proto construct is saying "I might call this as a class method" (a constructor of a brand new object) or "I might call this as an instance method" creating a copy or clone of an object. Those two ways of calling the method seem related, but conceptually they usually are not. If you need an instance method to create a clone, then write an instance method rather than trying to override the behavior of the constructor.

    You also have to consider that other programmers may have to maintain your code. Remember that OO programming, at it's heart, is to simulate a system (the first OO language was even called "Simula"). As such, it really should model something in an intuitive fashion. That is, in my opinion, yet another reason not to confuse class and instance methods. It's relatively little work to separate those out and make a cleaner interface.

    Hope that helps.

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

•Re: Re: (Ovid - minor code nits) Re: Adding autoloaded methods to symbol table with using strict refs
by merlyn (Sage) on Feb 26, 2002 at 21:50 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (4)
As of 2024-03-19 06:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found