【问题标题】:Mixing-in roles in traits apparently not working在特征中混合角色显然不起作用
【发布时间】:2019-05-01 07:58:14
【问题描述】:

这个例子取自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;

这返回Any,没有任何类型的角色混合。显然没有办法进入“doc”部分,尽管特征没有错误。有什么想法吗?

【问题讨论】:

  • 我认为 JJ 最感兴趣的是混入机制。但是对于那些对其他方式感兴趣的人来说,attaching Pod to declarators(然后可以通过.WHY 检索)应该可以用于将 doc 附加到变量。也就是说,Pod 设计的实现仍在进行中,目前它似乎只适用于参数,例如。 my &amp;sub = -&gt; ␤ $bar #= hi ␤ {} ␤ say &amp;sub.signature.params[0].WHY; # hi,并且该代码中的 ␤s(imo 也很重要)。
  • (顺便说一句,Variables doc page 表示“变量的运行时类是 Scalar。”一些 P6 中的变量绑定到 Scalars,但有些而是绑定到标量 - 小写的 's' - 例如 42 和许多其他到 Arrays、Hashs 等)

标签: traits raku


【解决方案1】:

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

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

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

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

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

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

  • 如果变量是Scalar,则需要.VAR以及来获取变量而不是它包含的值。 (如果不是 Scalar 也无妨。)

松了一口气?

我会在一月内更详细地解释上述内容,但首先,缓解一下如何?

也许我们可以引入一个新的Variable 方法来处理.var.VAR。但是 imo 这将是一个错误,除非该方法的名称非常好,它基本上消除了在此答案的下一部分中对 $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的加长解释

但是$v 已经是一个变量。为什么会有这么多varVARs?

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

不,因为Variable

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

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

  • 使编译器和编写特性的人的生活更轻松。但它要求特征编写者访问变量的用户视图以访问或更改用户视图。 Variable.var 属性存储该用户的视图。 (我注意到烘烤测试有一个您省略的.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

我所做的只是将.var 添加到... does role ... 行。在您的原始文件中,该行正在修改变量的编译器视图,即绑定到$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

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

如果一切顺利,这将是这个答案的结束。

一个错误

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

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

我们会看到:

Cannot assign to an immutable value

正如@guifa 所说,I stumbled on a while back

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

【讨论】:

    【解决方案2】:

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

    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")
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-08
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多