When I make a class method that starts with a ^
, and I try to invoke it, it gives me an error.
class C {
method ^test () {
"Hi"
}
}
dd C.new.test;
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
If I make the same method without a leading ^
, it works fine.
class C {
method test () {
"Hi"
}
}
dd C.new.test;
"Hi"
I've seen modules expose classes with methods that do start with a ^
, which leads to my question. Why am I getting this error when I define a method name that starts with a ^
?
The __call__ method enables Python programmers to write classes where the instances behave like functions and can be called like a function. When the instance is called as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x. __call__(arg1, arg2, ...) .
We can have a method name same as a class name in Java but it is not a good practice to do so. This concept can be clear through example rather than explanations. In the below example, a default constructor is called when an object is created and a method with the same name is called using obj.
To call a class method, put the class as the first argument. Class methods can be can be called from instances and from the class itself. All of these use the same method. The method can use the classes variables and methods.
When a method is invoked (called), a request is made to perform some action, such as setting a value, printing statements, returning an answer, etc. The code to invoke the method contains the name of the method to be executed and any needed data that the receiving method requires.
TL;DR The method is getting invoked properly. The ^
in foo.^bar
indicates a "metamethod". This is neither an instance method nor a class method. Metamethods are passed both an invocant, as is the case for all methods, and another "original invocant" object as the first argument.
Most users will never need to think about this stuff. But you've asked, so let's dig in...
Quoting the Meta-object protocol (MOP) Raku doc page:
Raku is built on a meta object layer.
This MOP layer defines various built in "metamethods" that you can use.
For example:
say .^attributes given class bar { has Int $!foo }
This displays (Int $!foo)
. The .^attributes
method call is a metamethod. It's called on the (typically invisible) metaobject that determines how a Raku type works behind the scenes. In this case, it returns the attributes (has
variables) of a class.
But there can also be user defined metamethods. One way to declare these is in an otherwise ordinary class:
class {
has Int $!foo;
method ^attributes ($arg) { self, $arg }
}
say baz.^attributes
The above baz
class includes an ^attributes
metamethod declaration that overrides the built in metamethod. Of special note, I've added an argument. ALL metamethods get at least one argument (in addition to a regular invocant).
With this declaration, instead of (Int $!foo)
in response to an .^attributes
call you instead get the list self, $arg
from the .^attributes
method in the baz
class.
Note how self
is neither a baz
instance object nor a baz
type object -- instead it's Perl6::Metamodel::ClassHOW+{<anon>}.new
-- whereas $arg
is a baz
(type) object.
The rest of this answer explains what's going on in more detail.
First, let's recap a typical method call.
The syntax foo.bar
results in a "bar" method (message) being dispatched to foo
.
If foo
is an instance of a class, "bar" is dispatched to that instance. Such a method call is sometimes referred to as an "instance method".
If foo
is a type object corresponding to a class, "bar" is dispatched to that type object. Such a method call is sometimes referred to as a "class method".
In both cases, "bar" is dispatched to foo
.
#foo.^bar
The syntax foo.^bar
is different.
Read the ^
as pointing up to another object that hovers invisibly above foo
, or indeed anything related to the kind of type foo
is.
Such objects are HOW
objects that determine How Objects Work. These HOW
objects typically stay invisible, making things work nicely, with users blissfully unaware of their existence and the work they're doing.1
A method call of the form foo.^bar
ordinarily results in Raku dispatching a metamethod call to foo
's HOW
object.
These metamethods require two invocant-like arguments. There's the HOW
object. This is passed as the regular invocant. Then there's the foo
object. This is passed as a first ordinary argument to the metamethod.
So that's what ordinarily happens when you call foo.^bar
-- a metamethod call is dispatched to foo
's HOW
object and foo
is passed as an ordinary argument storing what could be said to be "the original invocant".
#foo.^bar
when there's no built in .^bar
metamethod
If you call foo.^bar
when there's no such method you'll get an error:
42.^bar
yields:
No such method 'bar' for invocant of type 'Perl6::Metamodel::ClassHOW'
Note how the invocant type is a metamodel class, not 42
or Int
.
If a user defined class declares a ^.bar
, then Raku calls it, passing the instance/class's HOW
object as the invocant and the "original invocant" (foo
) as the first ordinary argument:
class foo {
method ^bar ($arg) { self, $arg }
}
say foo.^bar; # (Perl6::Metamodel::ClassHOW+{<anon>}.new (foo))
1 Calling .HOW
on an object returns its HOW
:
say .HOW given class {} # Perl6::Metamodel::ClassHOW
HOW
objects are part of the MOP, a layer deep down inside Raku.
Most devs will never need to explicitly dig down to this level.
If you dig even deeper you're leaving specified Raku. In Rakudo the .HOW
of a HOW
object is typically an NQP object:
say ((.HOW.new given class {}).HOW).^name; # NQPClassHOW
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With