在 Perl 6 中如何向现有 class 添加方法?

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

Int class 有一个方法 is_prime,所以我想,为了好玩,我想为我的一些爱好项目添加一些其他方法到 Int做数论的东西。

我以为我可以做这样的事情:

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

say 137.is-even;

但这不起作用:

===SORRY!===
P6opaque: must compose before allocating

我不知道这是否意味着我不能这样做或者我做的不正确。

我可以很容易地创建一个继承自 Int 的新 class,但这不是我感兴趣的:

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

my $n = MyInt.new(138);
say $n.is-even;

我不是在寻找解决方法或替代解决方案。

嗯,这行得通,我想我以前试过,而且我比我在问题中提出的更喜欢。

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

say 137.is-even;

不过我不确定这是否可行。 add_method docs 说我们应该只在类型被组合之前这样做。如果我调用 Int.^methodsis-even 不会出现。不过,它似乎是可以调用的,并且在做正确的事情。

词法

玩得更多,我想我可以创建一个不附加到任何 class 的方法并在对象上调用它:

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

这构造了一个Callable(看&is-even.WHAT)。在签名中,我将它限制为一个确定的 Int 值 (Int:D),但没有给它命名。我在类型约束后添加了冒号,以表明第一个参数是调用者。现在我可以将该方法应用于我喜欢的任何对象:

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

这在不同的维度上很好,因为它是词法的,但不好的是错误类型的对象可能会调用它。在它认为它有分配给的方法后出现错误。

这里有语法糖 - augment:

use MONKEY-TYPING;

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

增强 class 被认为是危险的有两个原因:首先,远距离行动,其次,因为(据我所知),有可能 未定义的行为 去优化 因为它可能会使各种方法缓存处于无效状态。

因此,在允许您使用之前提供 MONKEY-TYPING pragma 的要求。

顺便说一句,请注意 is-even 可以更紧凑地写为 self %% 2

如果您只需要 class 的某些实例,还有另一种有趣的方法可以做到这一点。您可以用角色装饰对象:

 my $decorated = $object but role { ... }

您也可以通过包含 & sygil:

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

当然,这只是语法糖,但我已经做过几次,因为它看起来比简单地调用 sub 更具可读性。

(我知道您“不是在寻找解决方法或替代解决方案”,但您自己发布了一些,所以我想,为什么不呢?)