代码之家  ›  专栏  ›  技术社区  ›  codesections

`在Raku中将Role混合到对象中时,会使用`verse`和`but`运算符

  •  0
  • codesections  · 技术社区  · 4 年前

    如果我有 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 因此可用于编译时混合(例如。, class C does R {} )然而 but 仅适用于运行时混合。我也明白 但是 可以是 used with an object (例如。, my $c = 'question' but False )然而 只能与a一起使用 角色 我不是在问这些差异;我唯一的问题是,当两者在运行时与 角色 我已经阅读了 documentation section on mixing in Role ,但没有看到答案。

    0 回复  |  直到 4 年前
        1
  •  11
  •   user0721090601    4 年前

    简单地说:

    • does 就地修改对象(应谨慎使用值类型,请参阅下面的注释)

    • but 返回一个新对象。

    当从字面量创建时,它可能不那么明显,但当与另一个对象一起使用时,我认为很明显:

    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
    

    注意,我作弊了一点,换了顺序 但是 .如果我保留了你的订单 将修改 $question 到位,应用角色,这意味着 但是 会克隆 $问题 (及其角色)并应用该角色(再次!):

    my $does = $question does R;
    my $but  = $question but  R;
    
    say $does.WHAT;  # (Str+{R})
    say $but.WHAT;   # (Str+{R}+{R})
    

    这是因为 因为运算符在概念上类似于 ++ += 也就是说,设计用于独立环境,例如

    my $foo = …;
    given $bar {
       when 'a' { $foo does A }
       when 'b' { $foo does B }
       when 'c' { $foo does B }
    }
    

    使用 但是 在概念上更接近于使用 $foo + 1 除非分配给或传递给其他东西,否则大多毫无意义。

    警告 和值类型

    如果你使用 在a 值类型 (主要是字符串和数字),你极有可能造成意想不到的副作用。这是因为值类型(例如字符串)应该是不可变的,并且可以相互替换。注意以下事项:

    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,做 但是 给你很好的恒定时间,不会污染字面意思。AFAICT,这种行为似乎是完全有效的,但肯定会让你措手不及。