Day 14 – nextsame and its cousins

Maybe you’re familiar with the way the keyword super in Java allows you to delegate to the method (or constructor) of a base class. Perl 6 has something similar… but in a world with multiple inheritance and mixins it makes less sense to call it super. So it’s called nextsame. Here’s an example of its use:

class A {
    method sing {
        say "life is but a dream.";
    }
}

class B is A {
    method sing {
        say ("merrily," xx 4).join(" ");
        nextsame;
    }
}

class C is B {
    method sing {
        say "row, row, row your boat,";
        say "gently down the stream.";
        nextsame;
    }
}

Now, when we call C.new.sing, our class hierarchy will output this:

row, row, row your boat,
gently down the stream.
merrily, merrily, merrily, merrily,
life is but a dream.

You’ll note how the call finds its way from C.sing via B.sing over to A.sing. Those transitions are (of course) mediated by the nextsame calls. You’ll note the similarity to, for example, Java’s super.

But calling along the inheritance chain is not the only place where nextsame proves useful. Here’s an example not involving object orientation:

sub bray {
    say "EE-I-EE-I-OO.";
}

# Oh right, forgot to add the first line of the song...
&bray.wrap( {
    say "Old MacDonald had a farm,";
    nextsame;
} );

bray(); # Old MacDonald had a farm,
        # EE-I-EE-I-OO.

So that’s another reason nextsame is not called super: it’s not necessarily related to the base class, because there might not be a base class. Instead, there’s some more general phenomenon involved. What might that be?

Every time we do a call to something, there’s a part of the language runtime making sure that the call ends up in the right routine. Such a part is called a dispatcher. A dispatcher makes sure that the following multi call ends up in the appropriate routine:

multi foo(    $x) { say "Any argument" }
multi foo(Int $x) { say "Int argument" }

foo(42) # Int argument

(And a nextsame in the second multi foo would re-dispatch to the first. But that doesn’t work in Rakudo yet.)

Dispatchers are everywhere in Perl 6. They’re involved in method calls, so that a method can defer along the inheritance chain, as we did in the beginning of the post. They’re in wrapped routines, so that the code doing the wrapping can call into the code being wrapped. And they participate in multi dispatch, so that multi variants can defer to each other. It’s all the same principle, but in different guises.

And nextsame is just a way to talk directly to your friendly neighborhood dispatcher. By the way, the keyword is called nextsame because it instructs the dispatcher to defer to the next candidate with the same signature. There are variants, as you’ll see below.

You can use nextsame in mixins, too:

class A {
    method foo { "OH HAI" }
}

role LogFoo {
    method foo {
        note ".foo was called";
        nextsame;
    }
}

my $logged_A = A.new but LogFoo;

say $logged_A.foo; # .foo was called
                   # OH HAI

I like this way to use mixins to inject behavior. I once wrote a post about it, and jnthn has written a Perl 6 module that exploits it.

Though pretty cool, this use of nextsame isn’t really anything new; in fact it’s just another example of the defer-along-the-OO-callchain dispatcher. That’s because mixing in the role LogFoo with but causes an anonymous subclass to be created, one that also does LogFoo. So role mixin nextsame boils down to just inheritance nextsame. (But we don’t need to actually grok that to use it, and it still feels slightly magical and very nice to use.)

In summary, nextsame works in a lot of places you’d expect it to work, and it works the way you expect it to. It defers to the next thing.

Oh, and nextsame has three closely related cousin keywords:

nextsame             Defer with the same arguments, don't return
callsame             Defer with the same arguments, then  return

nextwith($p1, $p2, ...) Defer with these arguments, don't return
callwith($p1, $p2, ...) Defer with these arguments, then  return

Naturally, the other three can be used in the same situations nextsame can.

4 thoughts on “Day 14 – nextsame and its cousins

  1. Im wondering if there is (or if there should be) a way of using nextsame in a functional way (other than with multisubs) with module inheritance. Say, if you have two modules:

    module A {
    sub foo {say “A::foo”}
    }

    module B is A {
    sub foo {say “B::foo”; nextsame}
    }

    Or is module inheritance like this not possible?

    1. “Module inheritance” is a bit of an oxymoron, since inheritance is a relation between classes, and becomes expressed through the dispatching of methods. A module of subs is twice removed from such action.

      I tried the above in Rakudo. It doesn’t work, but to my surprise the ‘module B is A’ declaration didn’t throw a Big Fat Error. I think it should (to catch misunderstandings like this early), so I submitted a rakudobug about it.

    1. Yes, we reply to non-fully-formed questions, too!

      multi foo(Any $x) { say "general" };
      multi foo(Int $x) { say "specific"; nextwith("") };
      foo(42);
      

      The above code will print specific and then general. Works in Rakudo today.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.