对象创建中的解构赋值

Destructuring assignment in object creation

与我的 一样,这是一个我无法判断我在理解 Raku 语义时是否遇到错误或漏洞的领域。上次结果是bug,怀疑闪电会打两下!

一般来说,我知道我可以将命名参数传递给一个函数,或者使用看起来很像创建 Pair 的语法(例如 f :a(42)),或者使用文档中 looks a lot like flattening a Hash (e.g., f |%h). (see argument destructuring 的语法).通常,这两个是等效的,即使对于非标量参数也是如此:

sub f(:@a) { dd @a }
my %h = a => [4, 2];
f :a([4,2]);  # OUTPUT: «Array element = [4, 2]»
f |%h;        # OUTPUT: «Array element = [4, 2]»

然而,当使用默认的 .new 构造函数构造对象时,这两种形式似乎给出了不同的结果:

class C { has @.a; }
my %h = a => [4, 2];
C.new: :a([4,2];  # OUTPUT: «C.new(a => ([[4, 2]])»
C.new: |%h;       # OUTPUT: «C.new(a => [[4, 2],])»

也就是说,传递 :a([4,2]) 会产生一个双元素数组,但使用参数展平语法会产生一个包含双元素数组的单元素数组。

这种行为是故意的吗?如果是这样,为什么?我是否可以使用语法来传递 |%h 并将双元素数组绑定到 @-sigiled 属性? (我知道使用 $-sigiled 属性是可行的,但我更喜欢 @ 的语义)。

Is this behavior intended?

是的。参数绑定使用绑定语义,而属性初始化使用赋值语义。对数组的赋值涉及 Scalar 个容器,Hash 的值是 Scalar 个容器。

If so, why?

直觉是:

  • 当调用一个函数时,我们不会在它 returns 之前做任何事情,所以我们可以有效地借出我们在它执行时传递给它的相同对象。因此绑定是一个合理的默认值(但是,可以在参数上使用 is copy 来获得赋值语义)。
  • 创建新对象时,它很可能会在构造函数调用之后继续存在。因此复制——即赋值——语义是一个合理的默认值。

And is there syntax I can use to pass |%h in and get the two-element Array bound to an @-sigiled attribute?

强制转换为 Map:

class C { has @.a; }
my %h = a => [4, 2];
say C.new: |%h.Map;

或者首先从 Map 开始:

class C { has @.a; }
my %h is Map = a => [4, 2];
say C.new: |%h;