in reply to (Re:)+ $class = ref $class || $class
in thread Constructor/Factory Orthodoxy
...but writing Packet_Foreign->new means that I have a priori knowledge that I always need a Packet_Foreign thingy. That is not the situation in the scenario at hand.
In the scenario under consideration, one is handed an object and needs another of the same class. Either one takes ref($obj) before calling new or one does it *in* new. The scenario does not admit a choice. One *could* pass the value to new as an argument, but that is just another variation (that may be productive) that I'm going to gloss over for now.
The code obscures the author's intent. Could he have written Obj->new but didn't just because of mindless imitation? Could he have written (ref $obj)->new but didn't just because of mindless imitation?
I give you rir's observation: When the code is bad, the documentation is worse. So I find your statements to rely on the documentation unconvincing. If truth and beauty don't exist in the code, I won't count on finding them in the documentation.
It is not reasonable to attribute use of the offending idiom to "mindless imitation". That steers the discourse toward a discussion of the discussor and away from the subject. The followup observation continues that unpleasant course.
I do not blindly accept that the idiom is, per se, a Bad Thing(tm). I do accept that it has the potential to create confusion. I do claim that proper documentation and the use of it by the programmer should mitigate that confusion. It is not that you are "unable to make your point to [me]"; it is that I do not blindly accept it as the One Truth.
More generally, what do you expect to happen if you call a class method as an object method? As developer of the module? As user of the module?
I see this whole issue as having a number of shades of gray, not just black and white.
yours,
Michael
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
(Re:)+ $class = ref $class || $class
by rir (Vicar) on Mar 04, 2003 at 22:39 UTC | |
I won't mention the author I meant to refer to because, on review, he didn't not really confess, he just strongly implied that he had rather thoughtless imitated this construct. I am unique, I am the only known confessed and repentant former thoughtless user of $class = ref $class || $class writing Packet_Foreign->new means that I have a priori knowledge that I always need a Packet_Foreign thingy. That is not the situation in the scenario at hand. That was the scenario with the code I posted. We seem to keep referring to different scenarios and aspects of the issue. I am focused on the unclear use of $obj->new in place of Obj->new and that that muddies the meaning of $obj->new in most situations. You seem to point to $obj->new as an equivalent to (ref $obj)-new. All I've got out of your arguments is that you wish to save five keystrokes per call, ()ref, and spend more text in the documentation of the code instead. That seems ridiculous. It is not reasonable to attribute use of the offending idiom to "mindless imitation".
It is reasonable to consider it as a possibility. I am of fair intelligence
and have admitted to repeatedly doing so myself. You may have specific intent regarding the meaning of $obj->new but the poor use of the idiom means I have to be clued in by you to know your specific usage pattern and then track who is writing a piece of code. Given that you, and I, are doing so poorly in making our positions clear to one another just reinforces my feeling that the code should be as clear and informative as possible. In the absence of compelling reasons I'll choose to avoid creating confusion over creating it and then attempting to mitigate the confusion. I see colors and shades of grey also. Here I find no blending of shades. I have given a clear reason to habitually avoid $obj->new. You have just stated or referenced personal preferences, I don't find a reason for your preference in your posts. Here in two lines of code the problem can be seen.
More generally, what do you expect to happen if you call a class method as an object method?
This very discussion demonstrates that this is not a valid question for
Perl. Perl has functions and methods. I think of methods being called with
a first argument that specifies the class of the method and may have other
uses in the method. Perl makes no meaningful distinction
between class and instance methods, it is just a bit of syntax. (ref $obj)->routine( $obj) and $obj->routine As developer of the module?
In this discussion I have been more in the role of maintainer/evolutionary
expander of the client code using the classes containing the new.
As user of the module?
I hope that all the arguments are checked for validity as possible. The first argument of method just happens to set on the other side of a method's name. I don't see your questions as relevant to the issue at hand. For me it is about clarity, as in locality of information, not about following the object-orientation style of Smalltalk or Self or C++ or ...
It is interesting that our minds find differing aspects of this discussion
as being worthy of expansion. My thoughts run toward variations of Sorry -- Would have got back sooner but for network and server problems. | [reply] [d/l] [select] |
by tye (Sage) on Mar 06, 2003 at 00:14 UTC | |
Sorry, wrong.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. 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: 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: in favor of this: so that my users can see this: 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: I let you just do: 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 | [reply] [d/l] [select] |
by rir (Vicar) on Mar 06, 2003 at 06:25 UTC | |
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.
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
None. It is ridiculous, I don't see why you bring it up.
... more reasonable ...
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 as worse than
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. | [reply] [d/l] [select] |
by tye (Sage) on Mar 06, 2003 at 07:06 UTC | |
by rir (Vicar) on Mar 06, 2003 at 17:28 UTC | |
by herveus (Prior) on Mar 05, 2003 at 18:49 UTC | |
In the discussion, the point of contention is the use of the idiom ref($proto) || $proto (typically in a constructor), particularly to allow one to call the constructor with an object instead of a class name. Good arguments have been advanced and referred to as to why this practice should be avoided, or at least used with much greater deliberation than would appear typical. I am taking as axiomatic that the documentation of the module will adequately cover how one might call the constructor (as well as other methods), including some mention of what one might expect from calling it with an object instead of a package name. I do realize that I am subject to disappointment with that assumption. When I fussed about attributing choices to "mindless imitation", I was a bit unclear. The words I responded to seemed to claim that mindless imitation was the only explanation, and I objected to that apparent generalization. I make no claim to being free of that fault, myself. I think I have gotten better at recognizing it in myself. When I query about the calling class methods as object methods, I fully realize that the distinction is not inherent in Perl. However, if I have a subroutine in a package that expects an object that isa(__PACKAGE__) as its first argument, such a subroutine has the appearance of being an object method. If that subroutine expects the first argument to be a package name, then it has the appearance of a class method. If that "class method" uses that package name as data, such as blessing a new object, then it is fair to ask how one would expect that method to behave if that package name were actually an object. If the "class method" makes no use of that first argument, then it makes no difference whether it is called as a "class method" or as an "object method". Returning to my axiom, I expect the documentation to help me out here. Now, if the documentation says "don't you dare call the class methods with an object, or bad things will happen", you, the user, Have Been Warned. On a more concrete level, if I am faced with $obj->method, and I need to discern which instance of "method" is being called, I'm going to be in for some work, no matter what. If there are multiple modules that might have "sub method" in them, whether I say ref($obj)->method or not will not affect the order of magnitude of the search. A nitpick: (ref $foo)->method($foo) is not quite the same as $foo->method; the first passes two arguments -- the second but one. The additional argument in the first form does not provide additional information, but it is not quite correct to say that they "pass the same info". I suppose that part of my perspective is that I don't have any particular problem with the concept of calling "class methods" with objects. I have not yet come around to the viewpoint that overloading that particular semantic facet is necessarily a Bad Thing. As I noted earlier, we may simply have to agree to disagree on this point. I certainly may be no more than a stubborn git. I cannot deny the possibility.
yours, | [reply] [d/l] [select] |
by rir (Vicar) on Mar 05, 2003 at 22:19 UTC | |
I don't have any particular problem with the concept of calling "class methods" with objects Likewise. I don't have a problem with that, I don't find the distinction "class" method very useful in Perl. In the case of new giving the reader more information about the type of the object can ease the understanding of code using that object. The reason to make my suggested usage a habit is that code can grow, an intermediate call may be inserted into your code; or code-text may be added around your construct making pertinent info more remote; or the type-tree may expand making the code more abstract. if I am faced with $obj->method, and I need to discern which instance of "method" is being called, I'm going to be in for some work, no matter what. If there are multiple modules that might have "sub method" in them, whether I say ref($obj)->method or not will not affect the order of magnitude of the search. You are exactly right. But the writer who says (ref $obj->method is explicitly telling you that the search is necessary. And the writer who says $obj->new when he knew that Obj->new was equivalent is abusing you unnecessarily, certainly there are some cases where the abuse is very minor, until the code grows. A nitpick:(ref $foo)->method($foo) is not quite the same as $foo->method; the first passes two arguments -- the second but one. The additional argument in the first form does not provide additional information, but it is not quite correct to say that they "pass the same info". Counter-nit: As you say The additional argument ... does not provide additional information so the statement is correct. I was trying to avoid the word pass but everything else I thought of seemed to relate to informing the reader of the code, which seemed more misleading. I ... may be ... a stubborn git. I won't dispute this, we both have demonstrated, ahem, a willingness to argue at length over minor issues. In that willingness I see fortitude and some concern for others and for truth. I am trying to share a Good Thing. I am not finding new ways to be more convincing on this so I'll let the subject lie. I certainly am not going to have angina over this subject, I hope steves is not too worried for me. :-) | [reply] [d/l] [select] |
by dragonchild (Archbishop) on Mar 04, 2003 at 22:48 UTC | |
------ Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement. Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified. | [reply] |