Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Re: (jeffa) 3Re: My first stab at OO perl...

by Theseus (Pilgrim)
on Jul 16, 2002 at 17:54 UTC ( [id://182182]=note: print w/replies, xml ) Need Help??


in reply to (jeffa) 3Re: My first stab at OO perl...
in thread My first stab at OO perl...

So you agree that calling instance constructors is a bad thing, but you don't agree that inserting code to prevent it from being done(or at least return the same result as if it had been done correctly) is a good thing? That's exactly what I don't understand.

I personally would never call $newobject = $oldobject->new, but I can't say that nobody who uses a class I design won't, and I want to account for that.

I'd like to hear your thoughts on the subject(and I'd love to hear merlyn's, if he sees this thread).

Replies are listed 'Best First'.
Re: Re: (jeffa) 3Re: My first stab at OO perl...
by dragonchild (Archbishop) on Jul 16, 2002 at 19:42 UTC
    sub new { my $classname = shift; return undef if ref $classname; # Other stuff here. }
    Just because Perl doesn't do it automatically doesn't mean you can't write gatekeepers on your own functions.

    ------
    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.

      That is indeed an option, but I'm of the school of thought that it's better for a program or operation in a program to silently catch an error and correct it for the user than to just fail outright.
        That is indeed an option, but I'm of the school of thought that it's better for a program or operation in a program to silently catch an error and correct it for the user than to just fail outright.

        ...and I'm of the school of thought that a program should silently toil away doing it's appointed task until it encounters something it's not expecting, at which point, it should be noisy as hell. Fixing something for the end user is not always the best solution, because what if your notion of "fixed" is different than what the user had in mind? The road to crufty code becomes short indeed.

        On the other hand, if you want to subsribe to the "be liberal in what you accept and strict in what you output" philosophy (which is what I think you were getting at), that's fine, just don't do it silently. Document every feature that you have coded (as well as known limitations, bugs, etc...)

        thor

        Catch the error and return an error code, you mean. Don't make up for the failings of your user. Teach him/her the correct way to do things.

        ------
        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.

        But this assumes that your code can know what the user^W programmer (inserted later ... sorry) really wanted, which is a dangerous assumption. Why would I call new on an instance in the first place? Maybe it's a typo (I wanted the net method perhaps)? Maybe I really don't grok OO? Silently catching errors that arise at runtime through erroneous user input or network hiccups, etc. is one thing, silently catching a programmer's conceptual or didn't-read-the -API mistakes and correcting them is quite another.

        It is your code though. But if you're ever going to distribute OO modules, I think it would be wise to warn (warn =) the user à la "new called on instance, a copy was created (did you mean to use clone) ?"

        I mistrust all systematizers and avoid them. The will to a system shows a lack of integrity -- F. Nietzsche

(jeffa) 5Re: My first stab at OO perl...
by jeffa (Bishop) on Jul 16, 2002 at 19:27 UTC
      I've read that post, and that's why I said I would like to hear merlyn's thoughts. Basically, that post says the same thing I just said: You shouldn't ever need to call the new method on an instance.

      However, that doesn't mean that as the designer of a class, you can't provide the proper instructions to prevent an error when someone ELSE calls new on an object they got from your class, be it intentional or otherwise.

      Personally, I consider the following two statements to be equally as ridiculous:

      Don't use ref($proto) because users of your module/class should never call a constructor on an instance.

      Don't check to make sure open() succeeded because a user should always make sure they're running your program with the proper permissions to open a file.

      Either way, you're depending on someone else not to break your code, and that can't be good.

        " I've read that post, and that's why I said I would like to hear merlyn's thoughts."

        Um, those are merlyn's thoughts. ;) Did you mean 'my' thoughts? Let's open that can of worms then. :D

        At this point, the whole matter is strictly opinion. The way i see it, if someone uses one of my modules incorrectly, they didn't read the POD. That is how you provide proper instruction on how to use a class. If i want them to be able to clone a class, i will provide a clone() method, and more importantly, i will provide POD that shows them how to instantiate and, if applicable, clone my objects.

        And i do not agree with your two statements being equal. The main reason is because you have two distinct audiences. Someone who just uses a program is not required to have any knowledge of the language, but someone who programs against that language better have knowledge. Further more, someone who accesses the constructor on an instantiated object clearly does not know what they are doing. Unless we are talking about a factory [method] pattern, the constructor should be class method, not an instance method. Try doing this in Java ...

        class Foo { public static void main(String args[]) { Bar bar = new Bar(42); System.out.println(bar.foo); Bar baz = bar.Bar(2112); System.out.println(baz.foo); } } class Bar { public int foo; public Bar(int foo) { this.foo = foo; } }
        Does not work. Try Python:
        class Bar: def __init__(self, foo): self.foo = foo bar = Bar(42) print bar.foo baz = bar.__init__(2112) print baz.foo
        Does not work. Ruby?
        class Bar def kind=(f) @foo = f end def kind @foo end end bar = Bar.new bar.kind = 42 print bar.kind baz = bar.new baz.kind = 2112 print baz.kind
        Does not work. Why? Because in all of these languages, the constructor is built-in. Perl's 'constructor' is not. You should not be able to call a built-in constructor as an instance method. This is one of the reasons why i do not like to teach OO with Perl, but it also one of the reasons why i love to program OO with Perl. Correct me if am wrong, but i believe that Perl6 is going to provide a built-in constructor, called new:
        class User; my ($id, $name, $email); # look ma, no new!!! package main; my $u = User.new('abw', 'Andy Wardley', 'abw@kfs.org');
        So, you do what you want. That is what Perl5 is all about. As long as you know why you are doing what you are doing, all is well. But i still think trying to prevent someone from "incorrectly" using my Perl class is just plain silly. However, if you are really paranoid about such things, try this instead:
        package foo; use strict; use Carp; sub new { my ($class,$id) = @_; croak "can't call new as instance method" if ref($class); my $self = { id => $id || 42, }; return bless $self,$class; }
        Better to deny them than deceive them. ;)

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
        Except that open can fail as a result of all kinds of things not under the control of the program(mer). Calling a constructor in a way that is not documented in the API for a module... well, if it works the way you expected it to, that's an undocumented feature. If it works, but not quite the way you wanted it to, that's a bug (in the mind of the programmer, anyway). If it just plain doesn't work, that's what one expects having read the documentation.

        Personally, I'd go with the last of these. If your constructor contains a bit of unexpected magic, you may as well document it. And if you want to allow for $instance->new(), you'll have to painstakingly document whether this creates a new "empty" object, or whether it creates a copy of the instance it was called on. FWIW, I think of "new" as a class method no matter what, so to my mind providing a way to use new from an instance is just muddying the water.

        Notes on reply: my point is that you will need to document this behavior, otherwise there is no reason to include it. And I have no idea what this adds to the module or how it helps programmers. It seems only to add a layer of indirection, which Perl OO already has enough of (imho-- and hopefully this will improve in Perl 6).

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2024-03-28 08:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found