Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class method name with ^ doesn't get invoked properly

Tags:

raku

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 ^?

like image 647
Tyil Avatar asked Jan 11 '19 08:01

Tyil


People also ask

What is the __ call __ method?

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, ...) .

Can we call method with class name?

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.

How do you invoke a class method?

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.

What happens when a method is invoked?

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.


1 Answers

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...

Metamethods

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.

A recap of an ordinary method call

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))

Footnotes

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
like image 149
raiph Avatar answered Dec 28 '22 03:12

raiph