How do you add a method to an existing class in Perl 6?

自古美人都是妖i 提交于 2019-12-03 23:40:00

There's syntactic sugar for this - augment:

use MONKEY-TYPING;

augment class Int {
    method is-even() returns Bool:D {
        return False if self % 2;
        return True;
    }
}

Augmenting a class is considered dangerous for two reasons: First, action at a distance, and second, because (as far as I'm aware), there's potential for undefined behaviour deoptimization as it might leave various method caches in an invalid state.

Thus, the requirement for providing the MONKEY-TYPING pragma before you're allowed to use it.

As an aside, note that is-even could be written more compactly as self %% 2.

Huh, this works, which I thought I'd tried before and I like better than what I presented in the question.

Int.^add_method( 'is-even', method () returns Bool:D {
    return False if self % 2;
    return True;
    } );

say 137.is-even;

I'm not sure this should work, though. The add_method docs says we should only do this before the type is composed. If I call Int.^methods the is-even doesn't show up. Still, it seems to be callable and doing the right thing.

Lexical methods

Playing more, I figured I could make a method that's not attached to any class and call that on an object:

my &is-even = method (Int:D :) returns Bool:D { self %% 2 };

This constructs a Callable (look at &is-even.WHAT). In the signature, I constrain it to be a definite Int value (Int:D) but don't give it a name. I add the colon after type constraint to note that the first argument is the invocant. Now I can apply that method to any object I like:

say 137.&is-even;
say 138.&is-even;
say "foo".&is-even;  # works, although inside is-even blow up

This is nice in a different dimension since it's lexical, but not nice in that an object of the wrong type might call it. The error shows up after it thinks it has the method to dispatch to.

You can also simply call a sub like a method by including the & sygil:

sub is-even(Int $n) { $n %% 2 }
say 4.&is-even;  # True

It's just syntactic sugar, of course, but I've done it a few times when it looked more readable than simply calling the sub.

(I know you're “not looking for workarounds or alternate solutions”, but you posted some yourself, so I thought, why not?)

There's another interesting way to do this if you need it only for some instances of a class. You can decorate an object with a role:

 my $decorated = $object but role { ... }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!