提问者:小点点

混合角色的特质显然不起作用


这个例子取自roast,尽管它已经存在了8年:

role doc { has $.doc is rw }

multi trait_mod:<is>(Variable $a, :$docced!) {
    $a does doc.new(doc => $docced);
}

my $dog is docced('barks');
say $dog.VAR;

这将返回任何,没有混合任何类型的角色。显然没有办法到达“文档”部分,尽管特征没有错误。有什么想法吗?


共2个答案

匿名用户

(这个答案建立在@guifa的回答和JJ的评论之上。)

在变量特征中使用的习语本质上是$var. var.VAR

虽然大声说出来听起来很有趣,但也很疯狂。它不是,但它至少需要解释,也许需要某种认知/句法上的解脱。

以下是如何理解它的简要版本:

>

  • $var作为trait参数的名称是有意义的,因为它绑定到Variable,即变量的编译器视图。

    在给定编译器视图的情况下,需要. var来访问变量的用户视图。

    如果变量是Scalar,则是。还需要VAR来获取变量而不是它包含的值。(如果它不是Scalar也没有害处。)

    我将在mos中更详细地解释上述内容,但是首先,一些救济呢?

    也许我们可以引入一个新的Variable方法来执行. var.VAR。但是在我看来,这将是一个错误,除非该方法的名称非常好,它基本上消除了对$var.var.VAR咒语解释的需要,这将在本答案的下一节中进行。

    但是我怀疑这样的名字是否存在。我想出的每一个名字都在某种程度上让事情变得更糟。即使我们想出了一个完美的名字,它充其量也不值得。

    我被你最初例子的复杂性打动了。有一个istrait调用dotrait。所以也许需要一个例程来抽象这种复杂性和$var. var.VAR。但是无论如何,有一些现有的方法可以降低这种双重特征的复杂性,例如:

    role doc[$doc] { has $.doc is rw = $doc}
    my $dog does doc['barks'];
    say $dog.doc; # barks
    

    但是$v已经是一个变量了。为什么有这么多varVAR

    事实上。$v绑定到Variable类的实例。这还不够吗?

    不,因为变量

    >

  • 用于在编译变量时存储有关变量的元数据。(也许它应该被称为元数据-About-A-Variable-Reareding?开玩笑的。变量在特征签名中看起来很好,改变它的名字不会阻止我们需要使用和解释$var. var.VAR习语。)

    不是我们要找的droid。我们想要一个变量的用户视角。一个被声明和编译,然后被用作用户代码一部分的变量。(例如,$dog行中的说$dog…。即使它是BEGIN说$dog…,所以它在编译时运行,$dog仍然会引用绑定到用户视角容器或值的符号。它不会引用Variable实例,它只是与变量相关的数据的编译器视角。)

    让编译器和那些编写特征的人的生活更轻松。但它要求特征编写者访问变量的用户眼睛视图来访问或更改用户眼睛视图。变量. var属性存储了该用户眼睛视图。(我注意到烘焙测试有一个.容器属性,你忽略了它。这显然现在被重命名为.var。我的猜测是,这是因为变量可能绑定到一个不可变的值而不是容器,所以名称.容器被认为具有误导性。)

    让我们从原始代码的变体开始,然后继续前进。我将从$dog切换到@dog并删除中的VAR行:

    multi trait_mod:<is>(Variable $a, :$docced!) {
      $a does role { has $.doc = $docced }
    }
    
    my @dog is docced('barks');
    say @dog.doc; # No such method 'doc' for invocant of type 'Array'
    

    这几乎奏效了。一个微小的变化就奏效了:

    multi trait_mod:<is>(Variable $a, :$docced!) {
      $a.var does role { has $.doc = $docced }
    }
    
    my @dog is docced('barks');
    say @dog.doc; # barks
    

    我所做的只是将. var添加到…do角色…行。在您的原始代码中,该行正在修改变量的编译器视图,即绑定到$aVariable对象。它不会修改变量的用户视图,即绑定到@dogArray

    据我所知,现在一切都适用于数组和哈希等复数容器:

    @dog[1] = 42;
    say @dog;     # [(Any) 42]
    say @dog.doc; # barks
    

    但是当我们使用Scalar变量尝试时:

    my $dog is docced('barks');
    

    我们得到:

    Cannot use 'does' operator on a type object Any.
    

    这是因为. var返回用户眼睛视图变量通常返回的任何内容。使用Array你会得到Array。但是使用Scalar你会得到Scalar包含的值。(这是P6的一个基本方面。它工作得很好,但是在这些场景中你必须知道它。)

    因此,为了让它看起来再次起作用,我们必须添加几个。VAR也是如此。对于Scalar以外的任何东西,。VAR是一个“无操作”,因此它不会对Scalar以外的情况造成伤害来添加它:

    multi trait_mod:<is>(Variable $a, :$docced!) {
      $a.var.VAR does role { has $.doc = $docced }
    }
    

    现在Scalar案例似乎也可以工作:

    my $dog is docced('barks');
    say $dog.VAR.doc; # barks
    

    (我不得不在行中重新引入. VAR,原因与我必须将其添加到$a.var.VAR…行相同。)

    如果一切顺利,这个答案就结束了。

    但是有些东西坏了。如果我们尝试初始化Scalar变量:

    my $dog is docced('barks') = 42;
    

    我们会看到:

    Cannot assign to an immutable value
    

    正如@guifa所指出的,我不久前偶然发现:

    看起来,带有混合的Scalar不再成功地作为容器运行,并且赋值失败。目前在我看来,这就像一个bug。

  • 匿名用户

    不是一个令人满意的答案但也许你可以从中进步

    role doc { 
      has $.doc is rw;
    }
    
    multi trait_mod:<is>(Variable:D $v, :$docced!) {
      $v.var.VAR does doc;
      $v.var.VAR.doc = $docced;
    }
    
    say $dog;            # ↪︎ Scalar+{doc}.new(doc => "barks")
    say $dog.doc;        # ↪︎ barks
    $dog.doc = 'woofs';  #
    say $dog;            # ↪︎ Scalar+{doc}.new(doc => "woofs")
    

    不幸的是,这有些不对劲,应用特征似乎会导致变量变得不可变。