特征中的混合角色显然不起作用 [英] Mixing-in roles in traits apparently not working

查看:37
本文介绍了特征中的混合角色显然不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此示例取自 roast>,虽然它已经存在 8 年了:

role doc { has $.doc is rw }multi trait_mod:<is>(变量 $a, :$docced!) {$a 做 doc.new(doc => $docced);}我的 $dog 是 docced('barks');说 $dog.VAR;

这将返回 Any,没有混合任何类型的角色.显然没有办法进入文档"部分,尽管特征没有错误.有什么想法吗?

解决方案

(此答案基于 @guifa 的答案和 JJ 的评论.)

用于可变特征的习语本质上是$var.var.VAR.

虽然大声说起来很有趣,但看起来也很疯狂.不是,但它至少需要解释,并且也许需要某种认知/句法上的缓解.

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

  • $var 作为特征参数的名称是有意义的,因为它绑定到 Variable,一个编译器的眼睛变量视图.

  • 需要
  • .var 来访问给定编译器视图的变量的用户眼视图.

  • 如果变量是一个 Scalar 那么需要一个 .VAR 以及来获取变量而不是它的值包含.(如果它不是 Scalar 也没有坏处.)

有些缓解?

我将在一个 mo 中更详细地解释上述内容,但首先,如何缓解一下?

也许我们可以引入一个新的 Variable 方法来执行 .var.VAR.但是 imo 这将是一个错误,除非该方法的名称是如此之好以至于基本上消除了对本答案下一部分中的 $var.var.VAR 咒语解释的需要.>

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

我对您原始示例的复杂性感到震惊.有一个 is trait 调用了 does trait.所以也许需要一个例程来抽象复杂性和 $var.var.VAR.但是无论如何,现有的方法可以减少双重特征的复杂性,例如:

role doc[$doc] { has $.doc is rw = $doc}我的 $dog 做 doc['barks'];说 $dog.doc;# 吠叫

$var.var.VAR的详细解释

<块引用>

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

确实如此.$v 绑定到 Variable 类的一个实例.这还不够吗?

不,因为变量:

  • 用于存储元数据关于一个变量编译时.(也许它应该被称为 Metadata-About-A-Variable-Being-Compiled?开个玩笑.Variable 在 trait 签名中看起来不错,更改名称不会停止我们无论如何都需要使用和解释 $var.var.VAR 习语.)

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

  • 让编译器和编写 trait 的人的工作更轻松.但它要求 trait writer 访问变量的用户视角以访问或改变用户视角.Variable.var 属性存储用户的视图.(我注意到烘焙测试有一个您省略的 .container 属性.现在显然已重命名为 .var.我猜这是因为变量可能绑定到不可变值而不是容器,因此名称 .container 被认为具有误导性.)

那么,我们如何到达$var.var.VAR?

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

multi trait_mod:(变量 $a, :$docced!) {$a 有角色 { 有 $.doc = $docced }}我的@dog 是 docced('barks');说@dog.doc;# 'Array'类型的调用者没有这样的方法'doc'

几乎有效.一个微小的变化,它的工作原理:

multi trait_mod:(变量 $a, :$docced!) {$a.var 有角色 { 有 $.doc = $docced }}我的@dog 是 docced('barks');说@dog.doc;# 吠叫

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

据我所知,对于数组和散列等复数容器,现在一切正常:

@dog[1] = 42;说@dog;# [(任何)42]说@dog.doc;# 吠叫

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

my $dog is docced('barks');

我们得到:

不能在类型对象 Any 上使用does"运算符.

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

所以为了让这个出现再次工作,我们还必须添加几个.VAR.对于 Scalar 以外的任何东西,.VAR 是一个无操作",所以它不会对除 Scalar 之外的情况添加它:

multi trait_mod:(变量 $a, :$docced!) {$a.var.VAR 角色 { has $.doc = $docced }}

现在 Scalar 的情况似乎也可以工作了:

my $dog is docced('barks');说 $dog.VAR.doc;# 吠叫

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

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

一个错误

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

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

我们会看到:

不能赋值给一个不可变的值

正如@guifa 所指出的,我偶然发现了一段时间:

<块引用>

似乎带有 mixin 的 Scalar 不再成功地用作容器,并且赋值失败.这目前在我看来像是一个错误.

This example is taken from roast, although it's been there for 8 years:

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;

This returns Any, without any kind of role mixed in. There's apparently no way to get to the "doc" part, although the trait does not error. Any idea?

解决方案

(This answer builds on @guifa's answer and JJ's comment.)

The idiom to use in variable traits is essentially $var.var.VAR.

While that sounds fun when said aloud it also seems crazy. It isn't, but it demands explanation at the very least and perhaps some sort of cognitive/syntactic relief.

Here's the brief version of how to make some sense of it:

  • $var makes sense as the name of the trait parameter because it's bound to a Variable, a compiler's-eye view of a variable.

  • .var is needed to access the user's-eye view of a variable given the compiler's-eye view.

  • If the variable is a Scalar then a .VAR is needed as well to get the variable rather than the value it contains. (It does no harm if it isn't a Scalar.)

Some relief?

I'll explain the above in more detail in a mo, but first, what about some relief?

Perhaps we could introduce a new Variable method that does .var.VAR. But imo this would be a mistake unless the name for the method is so good it essentially eliminates the need for the $var.var.VAR incantation explanation that follows in the next section of this answer.

But I doubt such a name exists. Every name I've come up with makes matters worse in some way. And even if we came up with the perfect name, it would still barely be worth it at best.

I was struck by the complexity of your original example. There's an is trait that calls a does trait. So perhaps there's call for a routine that abstracts both that complexity and the $var.var.VAR. But there are existing ways to reduce that double trait complexity anyway, eg:

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

A longer explanation of $var.var.VAR

But $v is already a variable. Why so many var and VARs?

Indeed. $v is bound to an instance of the Variable class. Isn't that enough?

No, because a Variable:

  • Is for storing metadata about a variable while it's being compiled. (Perhaps it should have been called Metadata-About-A-Variable-Being-Compiled? Just kidding. Variable looks nice in trait signatures and changing its name wouldn't stop us needing to use and explain the $var.var.VAR idiom anyway.)

  • Is not the droid we are looking for. We want a user's-eye view of the variable. One that's been declared and compiled and is then being used as part of user code. (For example, $dog in the line say $dog.... Even if it were BEGIN say $dog..., so it ran at compile-time, $dog would still refer to a symbol that's bound to a user's-eye view container or value. It would not refer to the Variable instance that's only the compiler's-eye view of data related to the variable.)

  • Makes life easier for the compiler and those writing traits. But it requires that a trait writer accesses the user's-eye view of the variable to access or alter the user's-eye view. The .var attribute of the Variable stores that user's-eye view. (I note the roast test has a .container attribute that you omitted. That's clearly now been renamed .var. My guess is that that's because a variable may be bound to an immutable value rather than a container so the name .container was considered misleading.)

So, how do we arrive at $var.var.VAR?

Let's start with a variant of your original code and then move forward. I'll switch from $dog to @dog and drop the .VAR from the say line:

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'

This almost works. One tiny change and it works:

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

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

All I've done is add a .var to the ... does role ... line. In your original, that line is modifying the compiler's-eye view of the variable, i.e. the Variable object bound to $a. It doesn't modify the user's-eye view of the variable, i.e. the Array bound to @dog.

As far as I know everything now works correctly for plural containers like arrays and hashes:

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

But when we try it with a Scalar variable:

my $dog is docced('barks');

we get:

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

This is because the .var returns whatever it is that the user's-eye view variable usually returns. With an Array you get the Array. But with a Scalar you get the value the Scalar contains. (This is a fundamental aspect of P6. It works great but you have to know it in these sorts of scenarios.)

So to get this to appear to work again we have to add a couple .VAR's as well. For anything other than a Scalar a .VAR is a "no op" so it does no harm to cases other than a Scalar to add it:

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

And now the Scalar case also appears to work:

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

(I've had to reintroduce the .VAR in the say line for the same reason I had to add it to the $a.var.VAR ... line.)

If all were well that would be the end of this answer.

A bug

But something is broken. If we'd attempted to initialize the Scalar variable:

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

we'd see:

Cannot assign to an immutable value

As @guifa noted, and I stumbled on a while back:

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.

这篇关于特征中的混合角色显然不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆