这个例子取自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;
这将返回任何
,没有混合任何类型的角色。显然没有办法到达“文档”部分,尽管特征没有错误。有什么想法吗?
(这个答案建立在@guifa的回答和JJ的评论之上。)
在变量特征中使用的习语本质上是$var. var.VAR
。
虽然大声说出来听起来很有趣,但也很疯狂。它不是,但它至少需要解释,也许需要某种认知/句法上的解脱。
以下是如何理解它的简要版本:
>
$var
作为trait参数的名称是有意义的,因为它绑定到Variable
,即变量的编译器视图。
在给定编译器视图的情况下,需要. var
来访问变量的用户视图。
如果变量是Scalar
,则是。还需要VAR
来获取变量而不是它包含的值。(如果它不是Scalar
也没有害处。)
我将在mos中更详细地解释上述内容,但是首先,一些救济呢?
也许我们可以引入一个新的Variable
方法来执行. var.VAR
。但是在我看来,这将是一个错误,除非该方法的名称非常好,它基本上消除了对$var.var.VAR
咒语解释的需要,这将在本答案的下一节中进行。
但是我怀疑这样的名字是否存在。我想出的每一个名字都在某种程度上让事情变得更糟。即使我们想出了一个完美的名字,它充其量也不值得。
我被你最初例子的复杂性打动了。有一个is
trait调用do
trait。所以也许需要一个例程来抽象这种复杂性和$var. var.VAR
。但是无论如何,有一些现有的方法可以降低这种双重特征的复杂性,例如:
role doc[$doc] { has $.doc is rw = $doc}
my $dog does doc['barks'];
say $dog.doc; # barks
但是$v
已经是一个变量了。为什么有这么多var
和VAR
?
事实上。$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角色…
行。在您的原始代码中,该行正在修改变量的编译器视图,即绑定到$a
的Variable
对象。它不会修改变量的用户视图,即绑定到@dog
的Array
。
据我所知,现在一切都适用于数组和哈希等复数容器:
@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")
不幸的是,这有些不对劲,应用特征似乎会导致变量变得不可变。