I posted a few days ago about a dynamic inheritance problem I had encountered and the workaround I had adopted. Thanks to the feedback I received, I've switched to a new approach.

As I described, I was trying to use mixin classes to add combinations of optional functionality on top of a base class:

+---------+ +---------+ +---------+ | Filter | | Safe | | Base | +---------+ +---------+ +---------+ | | | +------------------+------------------+ v +-------------+ | Safe_Filter | @ISA=qw(Filter Safe Base); +-------------+

The problem lay in re-dispatching method calls via SUPER:: when a mixin class did not directly inherit from the base class.

As several people pointed out, there are alternatives to the SUPER mechanism, such as NEXT. Once I came to terms with the fact that the SUPER:: dispatch system is somewhat arbitrary, I realized that the easiest solution is just to use a custom method to perform the redispatch. If instead of calling $self->SUPER::method( @_ ) I had my mixins call $self->SUPER('method', @_ ), then I could intercept each of those calls and control which method got called next.

In the end I named this method NEXT, because it is reminiscent of the NEXT module, although the direct method call syntax avoids the need for AUTOLOAD.

For example, here's how the exception-catching mixin used to work:

BEGIN { push @MIXIN, "#line ".__LINE__.' "'.__FILE__.'"', "", <<'/' +} sub compile { eval { local $SIG{__DIE__}; (shift)->SUPER::compile( @_) }; } /

Here's the equivalent method using the new calls:

sub compile { eval { local $SIG{__DIE__}; (shift)->NEXT('compile', @_) }; }

The base class provides this implementation for the NEXT method:

sub NEXT { my ( $self, $method, @args ) = @_; my $calling_package = caller(0); my @classes = ref($self) || $self; my @isa; while ( my $class = shift @classes ) { push @isa, $class; no strict; unshift @classes, @{ $class . "::ISA" }; } while ( my $class = shift @isa ) { last if ( $class eq $calling_package ) } while ( my $class = shift @isa ) { next unless my $sub = $class->can( $method ); return &$sub( $self, @args ); } Carp::croak( "Can't find NEXT method" ); }

Does anyone know how to reliably extract the name of the calling method, so that I can ommit that from the calls to the NEXT method? The ( caller(0) )[3] can be hidden inside evals and such; do I just walk back through stack frames until I find one that's not an eval?

Thanks again for your feedback!

Replies are listed 'Best First'.
•Re: Solving the SUPER problem in Mixins with a Dispatch Method
by merlyn (Sage) on Oct 14, 2004 at 07:16 UTC
    Once I came to terms with the fact that the SUPER:: dispatch system is somewhat arbitrary
    It's not the case that the "SUPER" is "arbitrary". It's very specific. The problem is when you choose to use multiple inheritance. Most OO hackers say that multiple inheritance is generally evil. You should consider redesigning your system so that you are using composition and delegation instead of MI, and life would be better for you and the people who have to maintain your code.

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

      NB: Nothing in this is helpful for the solution of the problem, it's just some OO stuff I've been thinking about.

      I'd say the problem is a lack of specificity, not just when you call SUPER but in all methods calls. What does $self->shoot do? If $self is a soccer player then we can expect a ball to be kicked at the goal but if $self is a gunslinger then we can expect an injury. Problems arise when $self is a person who knows how to play football and shoot a gun. In this case, what is the meaning of $self->shoot? This sort of problem is why mixins are viewed as a bad thing but I think this can be solved the same way we've solved other name clashes.

      We have namespaces for so many things but not for methods. If we had namespaces for methods then we could write $self->SoccerPlayer::shoot. Of course we wouldn't actually want to write that because it's a pain, we'd want to write

      treat $self as SoccerPlayer; # do lots of soccer playery things with $self $self->shoot;
      and if our language is smart enough we won't even need to write the "treat as" most of the time, it will be obvious because this is a method for the SoccerPlayer mixin.

      Amazingly enough, depite being a huge bag of suckiness Borland's Object Pascal (the laguage inside Delphi) incorporates something vaguely like this, except without any smartness or help from the compiler. In it's interface dispatch setup you can tell it that method soccer_shoot implements shoot when we're being treated as soccerplayer and method gunslinger_shoot implements shoot when we're being treated as a gunslinger. Of course the designers then went and ruined it by not allowing mixins. They provide a way to disambiguate methods based on context but then limit your ways of sharing methods among classes.

      Delegation and composition lead to lots of boilerplate delegation methods (which you can autogenerate if you're lucky) and it leads to situations where the subobject method calls have to be passed in a reference to their parent object so that they can manipulate it. For example a gunslinger may have a reload() method which looks in the persons various pockets for bullets and loads the gun so the delegation method in Person looks like

      sub reload { $self->gunslingerimplementer->reload($self) }
      and the other reload method looks like
      sub reload($parent) { my $bullets = $parent->pockets->getbullets(6); $parent->gun->load($bullets); }
      Then there's the question of where does the gun live. If it lives inside the gunslinger object then it makes it hard for the parent to manipulate it. If it lives inside the person object then it's an extra bit of stuff I need to remember when I'm adding gunslingability to my person. With mixins, we can set it up so that we get a gun field along with the relevant methods.

      So it's possible to have mixins without the usual problems and I think it would work very nicely (SUPER would go up to correct chain of classes), except that no language that I know of implements namespaced-methods.

        Seems to me that method namespaces would be overkill for a problem that could be solved in one of several other ways.

        First, 'shoot' is a badly chosen method name for either the GunSlinger or SoccerFootballPlayer Mixin (hehe). The same problem would occur in real life (sic) if the gun-toting centre forward happened to be dribbling the ball into the enemies 18 yard box when the opposing goalie went for his deringer. Someone shouts "Shoot!". What does he do?

        Yes, it's a fairly unrealistic scenario, but then so is the program that requires the Person class to utilise both Gunslinger and SoccerPlayer mixins concurrently.

        It's like the 'diamond inheritance' problem described elsewhere, it not too hard to dream up simplistic examples of such problems, but I wonder how often they actually occur in real code?

        I am not a fan of MI either, but the problems I've encountered with it almost all stem from the complexity of designing Classes and Class hierarchies with MI. And the main one of those problems is that MI prevents you from getting on with the job at hand.

        Rather than being able to concentrate on designing a Class to do a particular function, you are forever scrabbling around the existing class hierarchy trying to find the best place to slot it in; decide how to best leverage existing code; attempting to re-use every vaguely similar piece of code in the library; and trying to construct the class in such a way that the next time someone wants to do something similar, they will be able to inherit as much common functionality from this class as possible.

        The upshot of this process is, that gazillions of hooks, stubs and extra layers are added in just incase. And experience shows that they rarely get used.

        MI just puts the design process into quagmire of terminal inter-dependancies.

        Even the most thoroughly well thought through Class hierarchies needs wholesale refactoring the moment someone comes up with a new requirement.

        The namespace clash problem with Mixins can most simply be solved by using less generic (more specific) naming conventions for method names. $person->shootBall; and $person->shootGun;, but rather than worrying incessantly about such problems at the design stage, it's probably expedient to wait until such conflicts actually arise.

        One possible solution to the potential problem, would be to have syntax available to allow Mixin method names to be aliased by calling(?) classes. Eg. Something like:

        my $person = new Person() is Footballer( shoot as shootBall ), is GunSlinger( shoot as shootGun ) ;

        I find it hard to imagine the scenario where a gun-toting footballer would need to generically dispatch to the 'shoot' method according to which roles he was playing at the time. It's much more likely that any code needing to call either the shootBall or shootGun methods is going to be explicitly coded.

        Whether this would hold true for (all) real code examples is an open question.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
      ... multiple inheritance is generally evil. You should consider redesigning your system so that you are using composition and delegation instead of MI, and life would be better

      I believe you. But could you point out any texts that actually make that case.

      Thanks.

      Be well.

      Most OO hackers say that multiple inheritance is generally evil. You should consider redesigning your system so that you are using composition and delegation instead of MI, and life would be better for you and the people who have to maintain your code.

      As I tried to explain in my earlier node, I agree with the central thrust of this position, but think of it as "generally" true rather than a hard-and-fast rule. (One could say that AUTOLOAD and string evals were "generally" evil, or at least "sometimes" evil, but of course that's just one layer of the onion, and everyone recognizes that there are cases in which those techniques are perfectly valid.)

      I explicitly did consider using composition and delegation instead of multiple inheritance, but decided that in this case mixin classes would be a more straightforward mechanism. A base class and collection of mixin classes is hardly a wild and wooly case of MI: there's explicit sequencing of classes, no diamond networks, and a well defined base class.

      In the specific case of Text::MicroMason, one of my goals was to keep the base class simple. (If it grows significantly I might have to take "Micro" out of the package name.) I certainly understand why HTML::Mason has separate classes for each activity (resolver, lexer, assembler, compiler, etc), but given the deliberately small scope of the MicroMason distribution, I selected this other technique, in which activity is a method which can be overridden by subclasses.

      So, yes, I know that in the general case MI is "considered harmful", but is there something about this particular implementation that strikes you as more evil than the average? Of course, if you have comments about specific ways in which my life could be better, I'm open to hearing them...

      Good post -- This is basically what I was failing to say so eloquently in the previous thread with my MetaThing example ... inheritance is the proverbial over-used hammer of Software design.

      As for texts that say that (someone wanted examples), well, this is mainly an opinion that one need to form by oneself, or you need to use languages that don't use inheritance as an uber-hammer. Java zealots use inheritance as an uber-hammer all the time, so the "texts" as you say are probably sending mixed messages.

      Compare and constrast (ack! sounds like high school), for instance, Ruby File I/O with Java File I/O, and which one makes you want to strangle a small furry animal. Keeping it simple, and knowing when to encapsulate is a good thing. I am actually able to (by reasonable use of encapsulation and a good object model), forego the need for mixins most of the time as well. KISS (Rock and Roll every night, party every day) design usually doesn't need it. The trick is encapsulating other classes and asking them to do things for you, rather than trying to have your one class "be" everything.

Re: Solving the SUPER problem in Mixins with a Dispatch Method
by stvn (Monsignor) on Oct 14, 2004 at 16:21 UTC

    At the risk of breaking ranks here, I will attempt to help actually answer your question, rather than lecture you about OO and the evils of MI (which no doubt you have already heard, and dont agree with (<sarcasm>persish the thought that one could disagree with the great OO idealouges</sarcasm>)).

    Does anyone know how to reliably extract the name of the calling method, so that I can ommit that from the calls to the NEXT method? The ( caller(0) )3 can be hidden inside evals and such; do I just walk back through stack frames until I find one that's not an eval?

    The docs for caller talk about how you could do this.

    With caller(EXPR), it returns some extra information that the debugger uses to print a stack trace. The value of EXPR indicates how many call frames to go back before the current one.
    ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask) = caller($i);
    Here $subroutine may be (eval) if the frame is not a subroutine call, but an eval. In such a case additional elements $evaltext and $is_require are set: $is_require is true if the frame is created by a require or use statement, $evaltext contains the text of the eval EXPR statement. In particular, for an eval BLOCK statement, $filename is (eval), but $evaltext is undefined. (Note also that each use statement creates a require frame inside an eval EXPR frame.) $subroutine may also be (unknown) if this particular subroutine happens to have been deleted from the symbol table.
    You will likely need to test a number of values to handle both the eval BLOCK and the eval EXPR and not have it get confused.

    While I understand your hesitancy with AUTOLOAD, it would remove this entire problem. Also if you use the technique I showed in my post to your previous thread, in which you create a SUPER:: sub-package (which can easily be defined inline in your Base class), then you need not worry about having AUTOLOAD in any of you "real" classes.

    -stvn
      At the risk of breaking ranks here, I will attempt to help actually answer your question ...

      *grin*

      Does anyone know how to reliably extract the name of the calling method, so that I can ommit that from the calls to the NEXT method?
      While I understand your hesitancy with AUTOLOAD, it would remove this entire problem.

      Perhaps we're not talking about the same "problem."

      The built-in syntax for an empty wrapper is:

      sub compile { (shift)->SUPER::compile(@_) }

      The syntax I'm currently using is:

      sub compile { (shift)->NEXT('compile', @_) }

      What I'm hoping for is to omit the method name and have it picked up from context:

      sub compile { (shift)->NEXT(@_) }

      All of the AUTOLOADed solutions seem like they'll still require the current method name to be specified:

      sub compile { (shift)->SUPER::DISPATCH::compile(@_) }

      Admittedly, it's a minor point, but if you're going to have a rule that says that you can't redispatch to a different method name, it seems gauche to make the caller write out the method name again.

        Perhaps we're not talking about the same "problem."

        You are right, we were not. Sorry, only had one cup of coffee when I wrote that (hence the sarcasm and bitterness). However, I have now had more than one cup of coffee and I think this might help you.

        #!/usr/bin/perl use strict; use warnings; { package MyObject::Base; sub NEXT { my ($self, @args) = @_; my ($package, $filename, $subroutine); my $i = 1; ($package, $filename, undef, $subroutine) = caller($i); while ($subroutine eq '(eval)' || $filename eq '(eval)') { ($package, $filename, undef, $subroutine) = caller(++$i); } print "NEXT was called from : $subroutine\n"; } package MyObject; our @ISA = ('MyObject::Base'); sub compile1 { (shift)->NEXT(@_) } sub compile2 { eval { (shift)->NEXT(@_) } } sub compile3 { eval { eval { (shift)->NEXT(@_) } } } sub compile4 { my $self = shift; eval '$self->NEXT(@_)' } sub compile5 { my $self = shift; eval 'eval "$self->NEXT(\@_)"' } + sub compile6 { my $self = shift; eval { eval { eval { eval 'eval { eval "\$self->NEXT(\@_)" }' } } } } } MyObject->compile1(); MyObject->compile2(); MyObject->compile3(); MyObject->compile4(); MyObject->compile5(); MyObject->compile6(); __OUTPUT__ NEXT was called from : MyObject::compile1 NEXT was called from : MyObject::compile2 NEXT was called from : MyObject::compile3 NEXT was called from : MyObject::compile4 NEXT was called from : MyObject::compile5 NEXT was called from : MyObject::compile6

        -stvn