1. Skipping the middle man

Perl's OO interface is pretty flexible, and lets you do things easily that are awkward if not impossible in other languages1. One of these things can be labeled skip the middle man. Consider:

use strict; package Grandpa; sub hello { my $class = ref $_[0] || $_[0]; print "How do you do, from $class.\n"; } package Dad; our @ISA = 'Grandpa'; sub hello { my $class = ref $_[0] || $_[0]; print "Hiya from $class!\n"; } package Me; our @ISA = 'Dad'; package main; my $me = bless {}, 'Me'; # look Ma, no constructor! $me->hello; __END__ % perl say_hello.pl Hiya from Me!
OK, now suppose that you liked Grandpa's hello better than Dad's. Well, you could override Me::hello by copying-and-pasting the code for Grandpa::hello, but Perl offers a simpler way; just skip the middle man:
$me->Grandpa::hello(); __END__ % perl say_hello.pl How do you do, from Me.
Pretty cool, IMO. The "::" makes it look like a procedural interface call, but it is a bona fide OO method call, with an implicit instance (or package name) as first argument and all; a rather different animal from the similar-looking (and wrong2) Grandpa::hello().

(It wouldn't surprise me if this middle-man-skipping business is one of those things about Perl's take on OOP that make OO purists run for the hills, and maybe it is a result of those concerns that super.super.hello() makes Java have a cow. Dunno.)

Update: Indeed, as adrianh, merlyn, and brian_d_foy point out below, as cool as the flexibility to do this may appear (and I readily admit it does so to me), it is not a very good thing to do from the point of view of OO design.

2. The SUPER gotcha

You could also invoke $me->Dad::hello() but in this case, since Me does not override hello, the end result would be identical to that of invoking $me->hello(). However, if Me were overriding hello it may want to invoke Dad::hello at some point instead of its own hello, even (in fact most commonly) within the code for hello itself:

package Me; # ... sub hello { my $self = shift; $self->Dad::hello(); print "I'm just a WILD AND CRAZY GUY!\n"; }
In general, however, it is good to avoid hard-coding class names (e.g. what if later on you decided that 'OldMan' was a more suitable name for the 'Dad' class?), so Perl offers a little help with the SUPER pseudo-class:
package Me; #... sub hello { my $self = shift; $self->SUPER::hello(); print "I'm just a WILD AND CRAZY GUY!\n"; }
Now Me's hello invokes its parent's hello irrespective of the parent's name (it gets slightly more complicated if Me has more than one Daddy, aka multiple inheritance, but I won't go there; grep for "SUPER" in perlobj if you want the unsavory details). But the point I want to make is that despite the syntactic similarities between $me->SUPER::hello() and $me->Grandpa::hello(), SUPER and Grandpa are fundamentally different: Grandpa is a class, and SUPER is a pseudo-class. Whatever this "pseudo" means in deep ontological terms, for everyday program it's enough that it serve as a reminder that SUPER gets its meaning with respect to the current package, and not (as the arrow would suggest) with respect to the object that invokes it. In particular, contrary to Grandpa , whose meaning remains constant irrespective of the identity of the current package (as long as it has been loaded), the meaning of SUPER varies with the package. If you try something like
package main; my $me = bless {}, 'Me'; $me->SUPER::hello;
perl will kick up a big fuss:
Can't locate object method "hello" via package "main" at hello.pl line + 27.
even though a call to $self->SUPER::hello was perfectly OK within Me's override of hello. This is the infamous SUPER gotcha. The moral of the story: if you want to use SUPER turn on the appropriate package if necessary:
package Me; $me->SUPER::hello; package main;

When it comes to "skipping the middle man", though, Perl offers no help with avoiding hard-coding class names; you have no choice. I.e. $me->SUPER::SUPER::hello() will induce a bovine delivery in Perl too. Come to think of it, the fact that Perl provides no SUPER::SUPER is probably a clue that the designers of Perl don't think that skipping the middle man is such a hot idea. But, as is characteristic of Perl, they leave you a way to do it, if you really want to.

1Whether this is a bug or a feature, either in Perl or in those other languages (they know who they are) is another matter.

2In this particular case, at least, because the method is counting on having an instance or package name as its first argument. It is possible to write methods that can do double-duty as functions (e.g. consider the methods of the justly famed CGI module). Personally, I prefer simple interfaces over flexible interfaces, so I avoid such double-duty function/methods.

the lowliest monk

Replies are listed 'Best First'.
Re: Skipping the middle man & the SUPER gotcha
by adrianh (Chancellor) on Apr 02, 2005 at 13:51 UTC
    (It wouldn't surprise me if this middle-man-skipping business is one of those things about Perl's take on OOP that make OO purists run for the hills, and maybe it is a result of those concerns that super.super.hello() makes Java have a cow. Dunno.)

    Kinda :-)

    It's not that you can do this in Perl (you can also do it in many other OO languages) but the fact that you want to do this is a sign that there is something seriously odd with the class hierarchy.

    If a subclass isn't sharing behaviour with a superclass it's probably not an isa relationship - so inheritance is probably not what you want.

      Seconded.

      There's also another very important rule that I got from "Smalltalk Best Practice Patterns" (by Kent Beck) (I believe), which should be required reading for anyone doing serious OO. Yeah, it's all about Smalltalk, but most of the rules apply to any late-binding OO language like Perl.

      The rule is that superclass calls should be made only to the same-named method. In other words, if you're in foo, you should never be calling SUPER::bar. This is the sign of a misunderstanding about SUPER and about objects in general. The point of SUPER is to extend a parent class behavior, not mix it up.

      Wanting to "call the code two levels up" or "call a method other than your own name" are both signs of muddled thinking or an extremely bad class design.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

        Thanks for the ref to the Beck book.

        Yeah, it's all about Smalltalk, but most of the rules apply to any late-binding OO language like Perl.

        Could you or some other knowledgeable monk clue me in about this business of "late-binding"? I googled the definition, but I'm having a much harder time finding some bird's-eye picture of the fundamental qualitative differences between OO languages that are late-binding and those that aren't. I vaguely gather that early-binding may be tied to strong typing, but this is just a guess.

        Wanting to "call the code two levels up" or "call a method other than your own name" are both signs of muddled thinking or an extremely bad class design.

        Yes, agreed, and I don't recall ever needing to do this in classes I have coded, but unfortunately one often has to work with class hierarchies designed by others. In fact, what motivated this meditation was a practical problem posted in SoPW; one simple solution to that problem amounted to "skipping the middle man".

        Without knowing much of the OO theory behind it, I've figured that the ability to "skip the middleman" is, like the goto LABEL facility, something to use sparingly, but nice to have available for the once in a blue moon when you really need it (otherwise Perl would have simply disallowed it). But in light of your and adrianh's comments, I regret having made the whole maneuver sound as innocuous as I did.

        the lowliest monk

        There's also another very important rule that I got from "Smalltalk Best Practice Patterns" (by Kent Beck) (I believe), which should be required reading for anyone doing serious OO.

        Totally agree. One of my favourite books.

        (and everybody should have to learn Smalltalk once in their life anyway :-)

Re: Skipping the middle man & the SUPER gotcha
by brian_d_foy (Abbot) on Apr 02, 2005 at 18:37 UTC

    It looks like your problem is that your inheritance is broken. In your example, Dad inherits from Grandpa, but then decides to replace hello() without calling the hello() in Grandpa. That's not a good thing necessarily. You may not see it in this very simple example, for for more complex real world things, Grandpa may need to run its hello() to do things to make everything else keep working. The sub-classes don't usually know about those things, and they shouldn't.

    After that, you create a Me class that inherits from Dad. When it calls hello(), it gets the one from Dad. That's the way it should be because you said Me inherits from Dad. You shouldn't get to peek inside the @ISA for Dad to skip a level to call Grandpa::hello(). The @ISA may change, and if it does, you're hanged yourself.

    Perl offers "no help in hard-coding method names" because you shouldn't be doing that. If you want to use inheritance, you should be inheriting the behaviour of the superclass. If you don't want that behaviour, why are you inheriting it instead of overriding it?

    You may think you want this in certain situations, but eventually you'll get burned. When you don't let the entire chain do what they want to do, something is going to get out of sync because code doesn't run when it should. That's a terrible thing to have to track down.

    Furthermore, if you're using SUPER:: outside of the internals of a class, you're probably not doing the right thing. A good design hides all that stuff at the user level.

    --
    brian d foy <brian@stonehenge.com>

      Agreed. (Please see my reply to merlyn.)

      But now, given such strong opinions against doing something like $me->Grandpa::hello() I am dumbfounded by the fact that Perl supports it. Why does it??? Was this just a bad idea that somehow got through in the design of Perl?

      the lowliest monk

        Given such strong opinions on multiple inheritance or data hiding or a myriad of other items that perl doesn't follow ... well, I think that tells you why. Because perl isn't dogmatic about anything.

        If you really want to do it, you can. Which is way better than certain other languages who think they know better than the programmer.

Re: Skipping the middle man & the SUPER gotcha
by Thelonious (Scribe) on Apr 02, 2005 at 16:52 UTC
    The "::" makes it look like a procedural interface call, but it is a bona fide OO method call, with an implicit instance (or package name) as first argument and all; a rather different animal from the similar-looking (and wrong) Grandpa::hello().

    I think that:

    $me->Grandpa::hello()

    is the same as:

    Grandpa::hello($me);

    which isn't OO. You can test it like so:

    use strict; package other; sub hello { print "otherwise @{[ref shift]}\n"; } package Grandpa; sub hello { my $class = ref $_[0] || $_[0]; print "How do you do, from $class.\n"; } package Dad; our @ISA = 'Grandpa'; sub hello { my $class = ref $_[0] || $_[0]; print "Hiya from $class!\n"; } package Me; our @ISA = 'Dad'; sub new { bless {} } sub hello { print "hello\n"; } package main; my $me = bless {}, 'Me'; # look Ma, no constructor! $me->Grandpa::hello; $me->other::hello; __END__ How do you do, from Me. otherwise Me

    hth

      I think that:
      You think that, but you think wrongly. {grin}

      If you have an arrow in your syntax (method call), it respects @ISA (keep searching if subroutine is not found) and unshifts the additional first parameter (class name or instance reference). If you don't have an arrow, you don't get that, because it's an ordinary subroutine call. So those are definitely not the same.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

        O Guru,

        If the method name is fully-qualified, how could @ISA be used to any effect? When using a fully-qualified method name, doesn't that routine get called regardless of what's in @ISA? (The "other" package isn't in @Me::ISA, in my example above, and yet &other::hello() gets called, and with $me as an argument.)

        -- A Humble Grasshopper