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

I have a bunch of methods in a base class, and cycle through some of them in a sub-class using can(), and I wanted to see if this would work to skip some of them:
package Foo; use strict; use warnings; sub foo { print "foo\n" } sub bar { my $class = shift; if ( my $f = $class->can("foo") ) { print "yes, I can foo\n"; $f->(); } } package Bar; use strict; use warnings; our @ISA = "Foo"; sub foo; package main; use strict; use warnings; Bar->bar(); # OUTPUT: yes, I can foo Undefined subroutine &Bar::foo called at ./test_foo line 17.
I know there are other ways to accomplish what I want, but what do you think would be the best way, and would you condsider the above behaviour to be a bug? For now, I'm just declaring an empty foo() function in Bar:
sub foo {}
Update: moved bar() to better simulate actual code.
Maybe "disinherit" would be a better word :-)

Replies are listed 'Best First'.
Re: Attempt to uninherit method
by chromatic (Archbishop) on Dec 21, 2007 at 00:59 UTC

    Uninheriting methods violates the Liskov Substitution Principle, which I consider a worse bug.

    Predeclaring a subroutine but not providing a body should do exactly what it does here, so that can() and AUTOLOAD() work together properly.

      After some brief "research", I saw this brief description of the Liskov Substitution Principle:
      Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.
      And I filled in the variables, and tried to figure out if the following violates the LSV, and is therefore a bug by your reasoning:
      Let is_red(x) be a property provable about objects x of type FireEngine. Then is_red(y) should be true for objects y of type YellowFireEngine where YellowFireEngine is a subtype of FireEngine.

      It just seems like the above is something you might want to do...override some attribute in a sub-type, yet Liskov won't let me do it. I guess I'm glad perl doesn't enforce the Liskov Substitution Principle...or am I completely misunderstanding?

        Subclasses should respond to the same methods as their superclasses. I thought you wanted to wipe out a method so a subclass didn't respond to it at all. If not -- if you just want to have a method do nothing -- then it's not a violation, and I misunderstood.

Re: Attempt to uninherit method
by runrig (Abbot) on Dec 21, 2007 at 01:00 UTC
    Aha. If I do something like this, it works (not that I think this is better than any other method):
    sub bar { my $class = shift; if ( my $f = $class->can("foo") ) { if ( ! defined(&$f) ) { print "foo is undefined\n"; return; } print "yes, I can foo\n"; Bar->$f(); } }
        Yep, and for my purposes (see post elsewhere in this thread), that's fine with me. Although according to the Liskov Substitution Principle, I'd be throwing exceptions that I shouldn't be (if the method is called independently though I don't expect it to be), which is a bad thing, although I don't mind in this case.

        And AUTOLOAD kind of breaks the whole "can()" thing anyway...(or is it the other way round?)

Re: Attempt to uninherit method
by dragonchild (Archbishop) on Dec 22, 2007 at 15:16 UTC
    The proper solution to your scenario is subroutines with empty bodies. That way, you're self-documenting that this method is not applicable to this subclass.

    Have you thought about having a class factory (separate from an object factory)? You can indicate which methods are (and are not) applicable to a given subclass and let the factory figure out which methods to install based on class hierarchies. The factory code wouldn't be too hard to write - Class::MOP gives you a ton of introspection methods that simplify this kind of code to an iterator (provided for you) and a hash of method names.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?