19

I was browsing the Perl 6 docs on the shift routine and saw this snippet:

Defined as:

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

I know :D means the Array is defined and not Any or Nil, but what's :D:? It's very hard to search for.

This section of the type signature docs contains more examples of the syntax, but does not (as far as I can tell) explain it.

2 Answers 2

17

The invocant of a method gets passed as an implicit first argument. If you want to use an explicit parameter within the signature (eg to add a type smiley like :D or just to give it a more descriptive name), you need to separate it with a : instead of a , from the rest of the parameter list. This is necessary even in case of an empty list so it can be disambiguated from a signature with a regular positional parameter.

Some more information can be found in the design documents.

6

Christoph's answer is already excellent. My answer is an attempt to provide some context with a small concrete example.

As Christoph states, in Raku the invocant of a method gets passed as an implicit first positional argument which is then available to the method's body as self:

class Person {
    has $.name;
    
    method greet( Person $B, $greeting = 'Hello' ) {
        self.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.»

If you wish to bind it to something else, then use the syntax method meth-name( invocant : param1, param2, ..., param3) { ... } where param1, param2, ..., param3 are the regular parameters (both positional and named ones) you declare in a method. As Christoph states, this syntax "is necessary even in case of [a paratemer-less signature] so it can be disambiguated from a signature with a regular positional parameter." Therefore:

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

You could go a step further and also type the invocant, not necessarily because it's needed but because it makes the method's signature more descriptive:

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

If you don't want the method greet to accepts type objects (e.g., Person) but instead only objects instance of the type (e.g., Person.new), then you can make use of the type smily :D. Thus:

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

The type smilies are :D (for Defined), :U (for Undefined), and :_ (this is the implicit smily for a type that uses neither :D nor :U).

If you remove the explicit invocant (and revert to using self) from the method's signature, then you'll end up with something similar to what you have in your question. Here I'm just using some whitespace to make it look less daunting:

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

Addendum:

In Raku, methods can be restricted to either be called only on a class's object instances (for object methods) or only on the class itself (for class methods); you just need to add the :D smily to the class name for object methods and the :U smily to the class name for class methods:

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

However, this isn't as general as could be so instead, you can use the compile-time variable ::?CLASS which determines the current class and thus do away with the need for putting the name of the class there. For example, to restrict greet to only be called on instance objects of Person:

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

Like always if you're confused by the colons, you can always put some whitespace between whatever thing a type smily is attached to and the remaining : to make things more obvious, as in:

method greet( ::?CLASS:D : Person $B, $greeting = 'Hello' ) {
    self.name ~ ": $greeting, " ~ $B.name ~ '.'
}
3
  • 1
    Great answer, the second code block says self.name but should probably say $A.name as self is not bound
    – cat
    Commented Jan 26, 2020 at 22:37
  • 1
    I would point out that ::?CLASS is there so that you don't have to put the name of the class there. (Which also works.) Commented Jan 27, 2020 at 15:00
  • @cat Thanks for pointing that out; I've corrected it. @BradGilbert I've pointed out why ::?CLASS is there. BTW, feel free to edit the answer if you think it could be further improved ;-). Thanks.
    – uzluisf
    Commented Jan 28, 2020 at 18:19

Not the answer you're looking for? Browse other questions tagged or ask your own question.