2

I have a class X with a subclass Y. X has a method calculate() that I'd like to override in Y with some additional behaviour, an if statement that, if it fails, call X.calculate(). In Python this would be accomplished with:

class X(object):
    def calculate(self, my_arg):
        return "Hello!"

class Y(X):
    def calculate(self, my_arg):
        if type(my_arg) is int and my_arg > 5:
            return "Goodbye!"
        return super(Y, self).calculate(my_arg)

How can I do this in Perl using the Moo module?

dmn
  • 965
  • 3
  • 13
  • 24

2 Answers2

3

As the docs point out:

No support for super, override, inner, or augment - the author considers augment to be a bad idea, and override can be translated:

around foo => sub {
  my ($orig, $self) = (shift, shift);
  ...
  $self->$orig(@_);
  ...
};

(emphasis mine)

#!/usr/bin/env perl

use strict;
use warnings;

package X;

use Moo;

sub calculate {
    return 'Hello!'
}


package Y;

use Moo;

extends 'X';

around calculate => sub {
    my $orig = shift;
    my $self = shift;

    if ( $_[0] > 5 ) {
        return $self->$orig(@_);
    }

    return 'Goodbye!';
};

package main;

my $y = Y->new;

print $y->calculate(3), "\n";
print $y->calculate(11), "\n";
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • 1
    Yes! Thank you, this is exactly what I needed! The docs are so terrible on this. I thought I needed to use `before` but even then I couldn't figure out how to do it! Thanks again for your help. Perfection. – dmn Jun 13 '17 at 22:34
0

This can be done in Perl via the SUPER:: pseudo-class, which is part of Perl's method resolution system. You just put it in front of the method-call. It does not work for class methods or function calls.

use strict;
use warnings;
use feature 'say';

package Foo;
use Moo;

sub frobnicate {
    my $self = shift;
    say "foo";
}

package Bar;
use Moo;
extends 'Foo';

sub frobnicate {
    my $self = shift;
    say "bar";
    $self->SUPER::frobnicate;    
}

package main;
Bar->new->frobnicate;

You can even use this to call each parent's method if you have multi-level inheritance.

package Grandparent;
sub foo { ... }

package Parent;
use parent 'Grandparent';
sub foo { $_[0]->SUPER::foo }

package Child;
use parent 'Parent';
sub foo { $_[0]->SUPER::foo }

This will subsequently call foo in Child, Parent and Grandparent.

simbabque
  • 53,749
  • 8
  • 73
  • 136
  • 1
    Re "*Both Moo and Moose support this*", It's part of Perl's method dispatching, so it's always available. – ikegami Jun 13 '17 at 21:32
  • Thanks for your help. Unfortunately this didn't work for me and I'm not entirely sure why... – dmn Jun 13 '17 at 22:35