将 Role 混合到 Raku 中的对象时,`does` 与 `but` 运算符
`does` versus `but` operators when mixing in a Role into an object in Raku
如果我有一个 Role
R
定义为:
role R { method answer { 42 } }
这两行之间有什么区别(如果有的话):
my $a = 'question' does R;
my $b = 'question' but R;
它们看起来非常相似:
say $a.answer; # OUTPUT: «42»
say $b.answer; # OUTPUT: «42»
say $a.WHAT; # OUTPUT: «(Str+{R})»
say $b.WHAT; # OUTPUT: «(Str+{R})»
这是不是存在不止一种方法™,而且这两个意思是一样的?还是我遗漏了细微的差别?
注:
我知道 does
是 both an operator and a trait and thus can be used when for compile-time mixins (e.g., class C does R {}
) whereas but
is only for runtime mixins. I also understand that but
can be used with an object (e.g., my $c = 'question' but False
) whereas does
can only be used with a Role
. I'm not asking about either of those differences; my only question is about whether there's a difference when both are used at runtime with a Role
. I have read the documentation section on mixing in Role,但没有看到答案。
简单地说:
does
就地修改一个对象(应该谨慎使用值类型,见下面的注释)
but
returns 一个新对象。
当从文字创建时,它可能不那么明显,但当与另一个对象一起使用时,我认为它很清楚:
role R { method answer { 42 } }
my $question = 'question';
my $but = $question but R;
my $does = $question does R;
say $question.WHAT; # (Str+{R})
say $but.WHAT; # (Str+{R})
say $does.WHAT; # (Str+{R})
say $question.WHERE; # 129371492039210
say $but.WHERE; # 913912490323923
say $does.WHERE; # 129371492039210 <-- same as $question's
注意我作弊了一点,调换了 does
和 but
的顺序。如果我保留了你的顺序,does
将修改 $question
,应用角色,这意味着 but
将克隆 $question
(及其角色)并应用角色(再次!):
my $does = $question does R;
my $but = $question but R;
say $does.WHAT; # (Str+{R})
say $but.WHAT; # (Str+{R}+{R})
这是因为 does
作为运算符在概念上类似于 ++
或 +=
,也就是说,设计用于独立上下文,例如
my $foo = …;
given $bar {
when 'a' { $foo does A }
when 'b' { $foo does B }
when 'c' { $foo does B }
}
使用 but
在概念上更接近于使用 $foo + 1
— 除非分配给或传递给其他东西,否则几乎没有意义。
does
和值类型
的警告
如果您在 值类型 (主要是字符串、数字)上使用 does
,您极有可能会导致意外的副作用。这是因为值类型(例如,字符串)应该是不可变的并且可以相互替代。请注意以下几点:
role Fooish { }
my $foo = 'foo';
$foo does Fooish;
say 'foo'.WHAT; # (Str+{Fooish})
这是在编译时发生的替换(因此它不会影响,例如,在运行时发生的 'foobar'.substr(0,3)
),但如果将它们放入循环中,可能会导致一些非常奇怪的效果:
role Fooish { }
my @a;
@a.push('foo' does Fooish) for ^10;
say @a[0].WHAT; # (Str+{Fooish}+{Fooish}+{Fooish}+{Fooish}+{Fooish}
+{Fooish}+{Fooish}+{Fooish}+{Fooish}+{Fooish})
应用多次滚动需要的时间越来越长,因此如果将其更改为 ^100000,请准备好等待一段时间。 OTOH,做 but
给你很好的恒定时间并且不会污染文字。 AFAICT,这种行为似乎是完全有效的,但绝对会让您出乎意料。
如果我有一个 Role
R
定义为:
role R { method answer { 42 } }
这两行之间有什么区别(如果有的话):
my $a = 'question' does R;
my $b = 'question' but R;
它们看起来非常相似:
say $a.answer; # OUTPUT: «42»
say $b.answer; # OUTPUT: «42»
say $a.WHAT; # OUTPUT: «(Str+{R})»
say $b.WHAT; # OUTPUT: «(Str+{R})»
这是不是存在不止一种方法™,而且这两个意思是一样的?还是我遗漏了细微的差别?
注:
我知道 does
是 both an operator and a trait and thus can be used when for compile-time mixins (e.g., class C does R {}
) whereas but
is only for runtime mixins. I also understand that but
can be used with an object (e.g., my $c = 'question' but False
) whereas does
can only be used with a Role
. I'm not asking about either of those differences; my only question is about whether there's a difference when both are used at runtime with a Role
. I have read the documentation section on mixing in Role,但没有看到答案。
简单地说:
does
就地修改一个对象(应该谨慎使用值类型,见下面的注释)but
returns 一个新对象。
当从文字创建时,它可能不那么明显,但当与另一个对象一起使用时,我认为它很清楚:
role R { method answer { 42 } }
my $question = 'question';
my $but = $question but R;
my $does = $question does R;
say $question.WHAT; # (Str+{R})
say $but.WHAT; # (Str+{R})
say $does.WHAT; # (Str+{R})
say $question.WHERE; # 129371492039210
say $but.WHERE; # 913912490323923
say $does.WHERE; # 129371492039210 <-- same as $question's
注意我作弊了一点,调换了 does
和 but
的顺序。如果我保留了你的顺序,does
将修改 $question
,应用角色,这意味着 but
将克隆 $question
(及其角色)并应用角色(再次!):
my $does = $question does R;
my $but = $question but R;
say $does.WHAT; # (Str+{R})
say $but.WHAT; # (Str+{R}+{R})
这是因为 does
作为运算符在概念上类似于 ++
或 +=
,也就是说,设计用于独立上下文,例如
my $foo = …;
given $bar {
when 'a' { $foo does A }
when 'b' { $foo does B }
when 'c' { $foo does B }
}
使用 but
在概念上更接近于使用 $foo + 1
— 除非分配给或传递给其他东西,否则几乎没有意义。
does
和值类型
的警告
如果您在 值类型 (主要是字符串、数字)上使用 does
,您极有可能会导致意外的副作用。这是因为值类型(例如,字符串)应该是不可变的并且可以相互替代。请注意以下几点:
role Fooish { }
my $foo = 'foo';
$foo does Fooish;
say 'foo'.WHAT; # (Str+{Fooish})
这是在编译时发生的替换(因此它不会影响,例如,在运行时发生的 'foobar'.substr(0,3)
),但如果将它们放入循环中,可能会导致一些非常奇怪的效果:
role Fooish { }
my @a;
@a.push('foo' does Fooish) for ^10;
say @a[0].WHAT; # (Str+{Fooish}+{Fooish}+{Fooish}+{Fooish}+{Fooish}
+{Fooish}+{Fooish}+{Fooish}+{Fooish}+{Fooish})
应用多次滚动需要的时间越来越长,因此如果将其更改为 ^100000,请准备好等待一段时间。 OTOH,做 but
给你很好的恒定时间并且不会污染文字。 AFAICT,这种行为似乎是完全有效的,但绝对会让您出乎意料。