What's the difference between :D and :D:?
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.
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' ) {
$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.»
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' ) {
self.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 ~ '.'
}