jeffa has asked for the wisdom of the Perl Monks concerning the following question:

Is there anything wrong/bad about declaring $self outside the scope of a constructor?
my $self = {}; sub new { my $proto = shift; my $class = ref($proto) || $proto; bless $self, $class; return $self; }
Why did I do this? Because it allows me to access $self in the class's 'private' methods without having to pass it:
sub public_method { my ($self,$args) = @_; $self->{foo} = $args; _do_private_stuff(); } sub _do_private_stuff { print $self->{foo}; }
as opposed to:
sub public_method { my ($self,$args) = @_; $self->{foo} = $args; $self->_do_private_stuff(); } sub _do_private_stuff { my $self = shift; print $self->{foo}; }
but since I have _NEVER_ seen this done before, I am led to believe that it is baaaaaad. . . (/me thinks about subclassing)

Now that I think about, this is one of those little quirks that has always confused me about Perl's OO: the fact the the class's properties are tucked away in an anonymous data structure (conventionally named self) which is in turn, conveniently tucked away inside a constructor.

Contrast that to Java's OO model where the properties are defined OUTSIDE of the constuctor:

//avert your eyes - Java code ahead class Ball { private Color color; public Ball(Color c) { this.color = c; } }
Also, if defining $self outside the constuctory is bad, does anyone know of another way to access $self in 'private' function without having to pass it around, or should I just bit the bullet and define a vi macro for '$self->'?

Thanks,
Jeff

p.s. when I say 'private', please don't think that I am trying to fake private methods in Perl - I am not concerned about that, I am just trying to find new ways to type less :P

Replies are listed 'Best First'.
(crazyinsomniac) Re: OOP Question - location of $self
by crazyinsomniac (Prior) on Apr 16, 2001 at 05:25 UTC
    It's bad.

    It makes $self a package wide variable, and if you have two objects of type YOUR::Package, $self will contain the value of the 2nd object, thus clobbering your first.

    In Java's OO model, when you create a new variable of type Foo, java will create a new set of all the Class variables, except static ones, and tie them to that variable. In effect, it will create a new instance of that class.

    This is not how perl works. Perl import variables into the namespace of a running program/process and does not create a new instance of global class variables, which java does do. You can achieve a similar effect by using eval (i believe), but this is not very efficient.

    In Perl OO, when you have a constructor, it creates a new variable everytime it's called, where as a global $self, is only created once, upon importation of the module into the namespace.

    You can implement the technique you describe if you don't use more than a single instance of an object of your package type, and anyone else using your program knows this. But, if you do that, calling your module object oriented is a funny, since you kind of suck the OO out of it.(What kind of OO is a single object OO?)

    <SHAMELESS_PLUG>
    In my latest post Morse::Code I implement an OO module which can optionally be set in OO less mode. You might wanna peruse the code, as I did take some time to write it, and feel I grasp the Perl OO with some authority %^)
    </SHAMELESS_PLUG>

    update:
    ok tilly, but the difference is fairly minute.
    Ok. Much appreciated.

     
    ___crazyinsomniac_______________________________________
    Disclaimer: Don't blame. It came from inside the void

    perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"

      Actually he has $self as a lexical variable whose scope happens to be fairly close to the scope of the package, but it isn't a global.

      Otherwise what you say is pretty much correct. (Also see my anon explanation.)

      UPDATE
      Before saying that the difference is minute, see my feedback to your Morse module. The difference is fundamental and appears to be the source of some serious misunderstandings on your part.

Re (tilly) 1: OOP Question - location of $self
by Anonymous Monk on Apr 16, 2001 at 05:29 UTC
    (Sorry about posting anon, my net connection is in sad shape right now and so I am using lynx.)

    Your example does have something seriously wrong with it. If you call the constructor multiple times, you will return the same object each time. You want multiple $self's. Take care of that and this will work, but with all of the usual caveats about using cheap globals for passing params. (You just made one of the classic mistakes associated with that.)

    So bad style, but it can work.

Re: OOP Question - location of $self
by dws (Chancellor) on Apr 16, 2001 at 10:14 UTC
    I'm wondering if there isn't an unstated agenda behind this problem: performance. I suspect that you may be worrying (prematurely) about how fast your code is going to run if you have to keep passing $this around to private methods. My advice is to defer that worry, and code cleanly (meaing, pass $this) until you need to optimize. Then, keep these rules in mind:
    The Three Rules of Optimization
    1. Don't.
    2. Don't.
    3. Before you do, profile.
    (It is late here, and the source of these rules escapes me. Can someone fill in a source?)
      I'm wondering if there isn't an unstated agenda behind this problem: performance.

      Wellll he did say that he just wanted to save typing, so we should probably take his word for it, but preaching against optimization isn't a bad thing.

      The Three Rules of Optimization
      ...
      (It is late here, and the source of these rules escapes me. Can someone fill in a source?)

      Not sure, but while we're on the subject...

      Two Rules of Optimization:  1. Don't do it.   2. (for experts only) Don't do it yet.

      "Optimization hinders evolution" - Alan J. Perlis

      "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." - Donald Knuth

Re: OOP Question - location of $self
by Fastolfe (Vicar) on Apr 16, 2001 at 20:38 UTC
    If you just want to avoid typing $self->other_method from with a sub method { ... } object method, unless you use shift to pull $self out of the argument list, remember that you can always just pass @_ to a function to pass arguments as-is. Thus, you could do:
    sub method { my ($self, $arg) = @_; # do stuff with $self perhaps other(@_); # equiv. to $self->other($arg) }
    But this is kind of goofy. It's clearer to write it out the correct way, so that you and others maintaining your code in the future don't ask themselves what in the hell you were doing:
    $self->other($arg);
    Does this help at all?
      Passing @_ is not only goofy, but it breaks inheritance. You won't be able to inherit from the class and override other. Consider   other(@_); It is a non-OO invocation. If other doesn't exist in the current class, it won't be searched for using the @ISA chain. You might want to do this to implement "restricted" (callable only within one class) functions, though it requires that all callers know that other is restricted. That can get messy. <p Now consider   $self->other($arg); If other doesn't exist within the calling class, it will be searched via @ISA, which is what you want when you're using inheritance.

        The original discussion was about "internal" routines. Those are not meant to be subject to inheritance. So avoiding inheritance for these routines was the whole point.

                - tye (but my friends call me "Tye")
(jeffa) Re: OOP Question - location of $self
by jeffa (Bishop) on Apr 16, 2001 at 23:51 UTC
    Thanks to all that helped. Funny story: last night after I submitted this question, I got in the rock sled and drove to a friend's house to participate in the weekly ritual of The Soprano's. On the way over, I thought to myself - "/me, why didn't you test that on multiple instances?"

    When I got home, I tested before looking to see a horde of responses, and sure enough . . .my internet connection was down until this morning. :)

    Once again, I should have tested more before asking, but this time I am glad I asked.

    Thanks!

    Jeff

    R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
    L-L--L-L--L-L--L-L--L-L--L-L--L-L--