带有^的类方法名称无法正确调用

Tyi*_*yil 6 perl6 raku

当我创建一个以a开头的类方法^,并尝试调用它时,它会给我一个错误.

class C {
  method ^test () {
    "Hi"
  }
}

dd C.new.test;
Run Code Online (Sandbox Code Playgroud)
Too many positionals passed; expected 1 argument but got 2
  in method test at .code.tio line 1
  in block <unit> at .code.tio line 1
Run Code Online (Sandbox Code Playgroud)

如果我在没有前导的情况下使用相同的方法^,它可以正常工作.

class C {
  method test () {
    "Hi"
  }
}

dd C.new.test;
Run Code Online (Sandbox Code Playgroud)
"Hi"
Run Code Online (Sandbox Code Playgroud)

我已经看到模块使用以a开头的方法公开类^,这导致了我的问题.当我定义以a开头的方法名称时,为什么会出现此错误^

rai*_*iph 8

TL; DR正确调用该方法.将^foo.^bar表示"元方法".这既不是实例方法也不是类方法.Metamethods既是一个调用者,也是所有方法的情况,另一个是 "原始调用者"对象作为第一个参数.

大多数用户永远不需要考虑这些东西.但是你问过,所以让我们深入研究......

元方法

引用元对象协议(MOP) P6文档页面:

Perl 6构建在元对象层上.

此MOP层定义了您可以使用的各种内置"元方法".

例如:

say .^attributes given class bar { has Int $!foo }
Run Code Online (Sandbox Code Playgroud)

这显示(Int $!foo).该.^attributes方法的调用是一个元方法.它被称为(通常是不可见的)元对象,它决定了P6类型在幕后的工作方式.在这种情况下,它返回类的属性(has变量).

但也可以有用户定义的元方法.声明这些的一种方法是在另一个普通的类中:

say .^attributes given class baz { has Int $!foo; method ^attributes ($arg) { self, $arg } }
Run Code Online (Sandbox Code Playgroud)

上面的baz类包含一个^attributesmetamethod声明,它覆盖了内置的元方法.特别值得注意的是,我添加了一个参数.所有metamethods至少得到一个参数(除了常规的调用者).

使用此声明,而不是(Int $!foo)响应.^attributes调用,而是self, $arg从类中的.^attributes方法获取列表baz.

注意如何self既不是一个baz实例对象,也不是一个baz类型的对象-而不是它的Perl6::Metamodel::ClassHOW+{<anon>}.new-而$arg baz(类型)的对象.

这个答案的其余部分更详细地解释了正在发生的事情.

普通方法调用的概述

首先,让我们回顾一下典型的方法调用.

语法foo.bar导致调度到"bar"方法(消息)foo.

如果foo是类的实例,则将"bar"分派给该实例.这种方法调用有时被称为"实例方法".

如果foo是对应于类的类型对象,则将"bar"分派给该类型对象.这种方法调用有时被称为"类方法".

在这两种情况下,都会发送"bar" foo.

foo.^bar

语法foo.^bar不同.

阅读^为指向到那个无形以上徘徊另一个对象foo,或者实际上涉及到什么样的类型foo是.

这些对象是HOW确定对象如何工作的对象.这些HOW对象通常保持隐形,使事情很好地工作,用户幸福地不知道它们的存在和它们正在做的工作.1

形式的方法调用foo.^bar通常导致P6分派元方法调用fooHOW对象.

这些元方法需要两个类似调用的参数.有HOW对象.这是作为常规调用者传递的.那就是foo对象.这是作为metamethod的第一个普通参数传递的.

这就是你调用时通常会发生的事情foo.^bar- 一个metamethod调用被调度到foo一个HOW对象,并foo作为一个普通的参数传递,存储可以说是"原始的调用者".

foo.^bar什么时候没有内置的 .^bar metamethod

如果你foo.^bar在没有这种方法时打电话,你会收到一个错误:

42.^bar
Run Code Online (Sandbox Code Playgroud)

收益率:

No such method 'bar' for invocant of type 'Perl6::Metamodel::ClassHOW'
Run Code Online (Sandbox Code Playgroud)

请注意,调用类型是元模型类,而不是42Int.

如果用户定义的类声明了a ^.bar,则P6调用它,将实例/类的HOW对象作为调用者传递,将"原始foo调用者"()作为第一个普通参数传递:

class foo {
  method ^bar ($arg) { self, $arg }
}

say foo.^bar; # (Perl6::Metamodel::ClassHOW+{<anon>}.new (foo))
Run Code Online (Sandbox Code Playgroud)

脚注

1调用.HOW一个对象返回HOW:

say .HOW given class {} # Perl6::Metamodel::ClassHOW
Run Code Online (Sandbox Code Playgroud)

HOW对象是MOP的一部分,是P6内部的一层.

大多数开发人员永远不需要明确地挖掘到这个级别.

如果你挖得更深,你就会离开指定的P6.在Rakudo中.HOW,HOW对象通常是NQP对象:

say ((.HOW.new given class {}).HOW).^name; # NQPClassHOW
Run Code Online (Sandbox Code Playgroud)