:D 和 :D: 有什么区别?

What's the difference between :D and :D:?

我在浏览 Perl 6 docs on the shift routine 时看到了这个片段:

Defined as:

multi sub    shift(Array:D )
multi method shift(Array:D:)

我知道 :D 表示 Arraydefined 而不是 AnyNil,但是 :D: 是什么?好难找。

This section 类型签名文档包含更多语法示例,但没有(据我所知)解释它。

方法的调用者作为隐式第一个参数传递。如果你想在签名中使用一个明确的参数(例如添加一个笑脸类型 :D 或者只是给它一个更具描述性的名称),你需要用 : 而不是, 来自参数列表的其余部分。即使在空列表的情况下,这也是必要的,因此可以从具有常规位置参数的签名中消除歧义。

可以找到更多信息 in the design documents

Christoph 的回答已经很好了。我的回答是试图通过一个具体的小例子来提供一些背景信息。

正如 Christoph 所述,在 Raku 中,方法的调用者作为隐式第一个位置参数传递,然后作为 self:

可用于方法的主体
class Person {
    has $.name;

    method greet( Person $B, $greeting = 'Hello' ) {
        $A.name ~ ": $greeting, " ~ $B.name ~ '.'
    }
}

my $john = Person.new(name => 'John');
my $dana = Person.new(name => 'Dana');
say $john.greet($dana, 'Good morning'); # «John: Good morning, Dana.»

如果您希望将其绑定到其他对象,请使用语法 method meth-name( invocant : param1, param2, ..., param3) { ... },其中 param1, param2, ..., param3 是您在方法中声明的常规参数(包括位置参数和命名参数)。正如 Christoph 所说,此语法 "is necessary even in case of [a paratemer-less signature] so it can be disambiguated from a signature with a regular positional parameter." 因此:

# Person A greets person B.
method greet( $A : Person $B, $greeting = 'Hello' ) {
    self.name ~ ": $greeting, " ~ $B.name ~ '.'
}

您可以更进一步,也可以输入调用者,不一定是因为它是必需的,而是因为它使方法的签名更具描述性:

# Person A greets person B.
method greet( Person $A : Person $B, $greeting = 'Hello' ) {
    $A.name ~ ": $greeting, " ~ $B.name ~ '.'
}

如果您不希望方法 greet 接受类型对象(例如 Person),而只接受类型的对象实例(例如 Person.new),则你可以使用 type 微笑:D。因此:

# Person A greets person B.
method greet( Person:D $A : Person $B, $greeting = 'Hello' ) {
    $A.name ~ ": $greeting, " ~ $B.name ~ '.'
}

type smilies:D(对于D定义),:U(对于Undefined)和 :_(这是既不使用 :D 也不使用 :U 的类型的隐式笑脸)。

如果您从方法的签名中删除显式调用者(并恢复使用 self),那么您最终会得到类似于您在问题中遇到的问题。在这里,我只是使用了一些空格来让它看起来不那么令人生畏:

method greet( Person:D : Person $B, $greeting = 'Hello' ) {
    self.name ~ ": $greeting, " ~ $B.name ~ '.'
}

附录:

在 Raku 中,方法可以限制为仅在 class 的对象实例(对于 对象方法)或仅在 class本身(对于class方法);您只需要将 :D 笑脸添加到对象方法的 class 名称,将 :U 笑脸添加到 class 方法的 class 名称:

method object-method( CLASSNAME:D : ) { ... }
method class-method( CLASSNAME:U : ) { ... }

但是,这并不像一般情况那样,相反,您可以使用编译时变量 ::?CLASS 来确定当前的 class,从而消除将class 的名称在那里。例如,限制 greet 仅在 Person 的实例对象上调用:

method greet( ::?CLASS:D: Person $B, $greeting = 'Hello' ) {
    self.name ~ ": $greeting, " ~ $B.name ~ '.'
}

像往常一样,如果您对冒号感到困惑,您总是可以在类型 smily 附加的任何内容和剩余的 : 之间放置一些空格,以使事情更明显,如:

method greet( ::?CLASS:D : Person $B, $greeting = 'Hello' ) {
    self.name ~ ": $greeting, " ~ $B.name ~ '.'
}