在特征中混合角色显然不起作用
Mixing-in roles in traits apparently not working
这个例子取自from 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;
这个returns Any
,没有任何类型的角色混入。显然没有办法到达"doc"部分,尽管特征没有错误。有什么想法吗?
不是一个令人满意的答案,但也许你可以从中取得进步
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")
不幸的是,这有点不对劲,应用特征似乎会导致变量变得不可变。
(此答案基于@guifa 的回答和 JJ 的评论。)
变量特征中使用的成语本质上是$var.var.VAR
。
虽然大声说出来听起来很有趣,但看起来也很疯狂。它不是,但它至少需要解释,并且 或许 某种 cognitive/syntactic 解脱。
以下是如何理解它的简短版本:
$var
作为 trait 参数的名称是有意义的,因为它绑定到 Variable
、compiler's-eye 视图一个变量。
.var
需要访问 用户视角 给定编译器视角的变量视图。
如果变量是 Scalar
则需要 .VAR
以及 来获取变量而不是它的值包含。 (如果不是 Scalar
也没什么坏处。)
松了一口气?
我会在 mo 中更详细地解释以上内容,但首先,一些缓解措施如何?
也许我们可以引入一种新的 Variable
方法来完成 .var.VAR
。但是我认为这将是一个错误,除非该方法的名称非常好,它基本上消除了对该答案下一节中的 $var.var.VAR
咒语解释的需要。
但我怀疑这样的名字是否存在。我想出的每一个名字都在某种程度上让事情变得更糟。即使我们想出了一个完美的名字,充其量也只是勉强值得。
我对您原始示例的复杂性感到震惊。有一个 is
特征调用 does
特征。因此,也许需要一个例程来抽象化这种复杂性和 $var.var.VAR
。但是无论如何都有现有的方法来降低双重特征的复杂性,例如:
role doc[$doc] { has $.doc is rw = $doc}
my $dog does doc['barks'];
say $dog.doc; # barks
$var.var.VAR
的详细解释
But $v
is already a variable. Why so many var
and VAR
s?
确实如此。 $v
绑定到 Variable
class 的实例。还不够吗?
没有,因为 Variable
:
用于存储元数据关于一个变量编译。 (也许它应该被称为 Metadata-About-A-Variable-Being-Compiled
?开个玩笑。Variable
在特征签名中看起来不错,改变它的名字不会阻止我们需要使用和解释 $var.var.VAR
习语。)
不是我们要找的droid。我们想要变量的用户视角。一个已经被声明和编译,然后被用作用户代码的一部分。 (例如,say $dog...
行中的$dog
。即使它是BEGIN say $dog...
,所以它在编译时运行,$dog
也会still 指的是绑定到用户眼视图容器或值的符号。它不会指代 Variable
实例,它只是 编译器眼视图 的数据 与变量相关。)
使编译器和那些编写特征的人的生活更轻松。但它要求特征编写器访问变量的用户视角,以访问或更改用户视角。 Variable
的 .var
属性存储该用户的视线。 (我注意到 roast 测试有一个你忽略的 .container
属性。现在显然已重命名为 .var
。我猜那是因为变量可能绑定到不可变值而不是容器,所以名称 .container
被认为具有误导性。)
那么,我们如何到达 $var.var.VAR
?
让我们从原始代码的变体开始,然后继续。我将从 $dog
切换到 @dog
并从 say
行删除 .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
我所做的就是在 ... does role ...
行中添加一个 .var
。在您的原始代码中,该行正在修改变量的 编译器视图 ,即绑定到 $a
的 Variable
对象。它不会修改用户对变量的看法,即 Array
绑定到 @dog
.
据我所知,现在一切都适用于数组和散列等复数容器:
@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
returns 不管用户的视线变量通常是什么 returns。使用 Array
你会得到 Array
。但是使用 Scalar
你会得到 Scalar
包含的值。 (这是 P6 的一个基本方面。它很好用,但你必须在这些场景中了解它。)
所以为了让这个 出现 再次工作,我们还必须添加一对 .VAR
。对于 Scalar
以外的任何东西,.VAR
都是 "no op",因此添加它对 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
(我不得不在 say
行中重新引入 .VAR
,原因与我必须将其添加到 $a.var.VAR ...
行的原因相同。)
如果一切顺利,本回答到此结束。
一个错误
但是有些东西坏了。如果我们尝试初始化 Scalar
变量:
my $dog is docced('barks') = 42;
我们会看到:
Cannot assign to an immutable value
正如@guifa 指出的那样,:
It seems that a Scalar
with a mixin no longer successfully functions as a container and the assignment fails. This currently looks to me like a bug.
这个例子取自from 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;
这个returns Any
,没有任何类型的角色混入。显然没有办法到达"doc"部分,尽管特征没有错误。有什么想法吗?
不是一个令人满意的答案,但也许你可以从中取得进步
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")
不幸的是,这有点不对劲,应用特征似乎会导致变量变得不可变。
(此答案基于@guifa 的回答和 JJ 的评论。)
变量特征中使用的成语本质上是$var.var.VAR
。
虽然大声说出来听起来很有趣,但看起来也很疯狂。它不是,但它至少需要解释,并且 或许 某种 cognitive/syntactic 解脱。
以下是如何理解它的简短版本:
$var
作为 trait 参数的名称是有意义的,因为它绑定到Variable
、compiler's-eye 视图一个变量。.var
需要访问 用户视角 给定编译器视角的变量视图。如果变量是
Scalar
则需要.VAR
以及 来获取变量而不是它的值包含。 (如果不是Scalar
也没什么坏处。)
松了一口气?
我会在 mo 中更详细地解释以上内容,但首先,一些缓解措施如何?
也许我们可以引入一种新的 Variable
方法来完成 .var.VAR
。但是我认为这将是一个错误,除非该方法的名称非常好,它基本上消除了对该答案下一节中的 $var.var.VAR
咒语解释的需要。
但我怀疑这样的名字是否存在。我想出的每一个名字都在某种程度上让事情变得更糟。即使我们想出了一个完美的名字,充其量也只是勉强值得。
我对您原始示例的复杂性感到震惊。有一个 is
特征调用 does
特征。因此,也许需要一个例程来抽象化这种复杂性和 $var.var.VAR
。但是无论如何都有现有的方法来降低双重特征的复杂性,例如:
role doc[$doc] { has $.doc is rw = $doc}
my $dog does doc['barks'];
say $dog.doc; # barks
$var.var.VAR
的详细解释
But
$v
is already a variable. Why so manyvar
andVAR
s?
确实如此。 $v
绑定到 Variable
class 的实例。还不够吗?
没有,因为 Variable
:
用于存储元数据关于一个变量编译。 (也许它应该被称为
Metadata-About-A-Variable-Being-Compiled
?开个玩笑。Variable
在特征签名中看起来不错,改变它的名字不会阻止我们需要使用和解释$var.var.VAR
习语。)不是我们要找的droid。我们想要变量的用户视角。一个已经被声明和编译,然后被用作用户代码的一部分。 (例如,
say $dog...
行中的$dog
。即使它是BEGIN say $dog...
,所以它在编译时运行,$dog
也会still 指的是绑定到用户眼视图容器或值的符号。它不会指代Variable
实例,它只是 编译器眼视图 的数据 与变量相关。)使编译器和那些编写特征的人的生活更轻松。但它要求特征编写器访问变量的用户视角,以访问或更改用户视角。
Variable
的.var
属性存储该用户的视线。 (我注意到 roast 测试有一个你忽略的.container
属性。现在显然已重命名为.var
。我猜那是因为变量可能绑定到不可变值而不是容器,所以名称.container
被认为具有误导性。)
那么,我们如何到达 $var.var.VAR
?
让我们从原始代码的变体开始,然后继续。我将从 $dog
切换到 @dog
并从 say
行删除 .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
我所做的就是在 ... does role ...
行中添加一个 .var
。在您的原始代码中,该行正在修改变量的 编译器视图 ,即绑定到 $a
的 Variable
对象。它不会修改用户对变量的看法,即 Array
绑定到 @dog
.
据我所知,现在一切都适用于数组和散列等复数容器:
@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
returns 不管用户的视线变量通常是什么 returns。使用 Array
你会得到 Array
。但是使用 Scalar
你会得到 Scalar
包含的值。 (这是 P6 的一个基本方面。它很好用,但你必须在这些场景中了解它。)
所以为了让这个 出现 再次工作,我们还必须添加一对 .VAR
。对于 Scalar
以外的任何东西,.VAR
都是 "no op",因此添加它对 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
(我不得不在 say
行中重新引入 .VAR
,原因与我必须将其添加到 $a.var.VAR ...
行的原因相同。)
如果一切顺利,本回答到此结束。
一个错误
但是有些东西坏了。如果我们尝试初始化 Scalar
变量:
my $dog is docced('barks') = 42;
我们会看到:
Cannot assign to an immutable value
正如@guifa 指出的那样,
It seems that a
Scalar
with a mixin no longer successfully functions as a container and the assignment fails. This currently looks to me like a bug.