在 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.^methods
,is-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 更具可读性。
(我知道您“不是在寻找解决方法或替代解决方案”,但您自己发布了一些,所以我想,为什么不呢?)
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.^methods
,is-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 更具可读性。
(我知道您“不是在寻找解决方法或替代解决方案”,但您自己发布了一些,所以我想,为什么不呢?)