Sunday, September 27, 2009

Multiple Inheritance

As an update to my previous post on subclassing PAST::Node classes, I ran into another problem. It turns out that the PAST -> POST compiler knows about, and depends on the differences between, the different Node subclasses.

For example, there are multi-methods that match on the types of their parameters, so that a PAST::Block gets processed by a different method than a PAST::Var. You get the idea.

So my original idea, of deriving a set of roughly parallel Block, Var, Val classes from a new root that was a child of PAST::Node won't work. Because while a Slam::Block may be a Slam::Node, which may be a PAST::Node, it turns out that it also has to be a PAST::Block for the other stuff to work.



So I'm stuck needing to "insert" my class behavior into several different places. I can't get between PAST::Block and PAST::Node, and I shudder to think about getting behind PAST::Node as a parent class.

Deriving from PAST::Block, Node, Var, Val, Stmts, Op, etc. seems like the only reasonable way to go, except that I would have to reimplement all the Slam::Node methods in each child, and would lose the convenience of common ancestry.

P6object to the Rescue!
But all is not lost, because among all the other cool things about Parrot is this: Parrot doesn't care about how many parents an object has. What's more, the P6object library supports adding parents willy-nilly.

So the solution, in this case, was pretty easy:
        my $base := $meta.new_class('Slam::Node');
       
        my $block := $meta.new_class('Slam::Block', :parent($base));
        $meta.add_parent($block, PAST::Block);
       
        my $test := Slam::Block.new();
        if $test.isa(PAST::Block) {
            say("I'm a blockhead, ma!");
        }
        if $test.isa(Slam::Node) {
            say("I'm a slammer, too.");
        }

Et voila! Multiple inheritance. Or, if you prefer, a "slatheron."

No comments:

Post a Comment