Can methods be treated as regular subroutines?
Asked Answered
E

2

10

Is it possible to assign methods to variables and pass them around as arguments inside the class similar to subroutines?

I know they're are accessible by either self. or self! (or whatever named explicit invocant) inside other methods but I'm wondering if it's possible to pass a method's code object as an argument to another method.

Eternity answered 15/7, 2019 at 13:47 Comment(1)
perl6 -e 'class Foo { method bar($a) { say $a } }; my &foosub = -> |c { Foo.^find_method("bar").(Foo, |c) }; &foosub(42)'Bordie
W
11

Have you considered passing names of methods around?

class Foo {
    method bar($a) { dd $a }
}
my $name = "bar";
Foo."$name"(42);  # 42

The syntax calls for the need of stringifications and parentheses to indicate you want to call that method. If you really want to use the Method object and pass that around, you can, but there is no real nice syntax for it:

class Foo {
    method bar($a) { dd $a }
}
constant &B = Foo.^find_method("bar");
B(Foo, 42)  # 42

In this example, the constant &B is created at compile time. But you can also call this method at runtime, e.g. when the name of the method is in a variable:

my $name = "bar";
my $method = Foo.^find_method($name);
$method(Foo, 42);

However, in that case, you would probably be better of with the ."$name"() syntax. See https://docs.raku.org/routine/find_method for a bit more info.

Wroth answered 15/7, 2019 at 19:52 Comment(1)
The ."$name"() is precisely what I was looking for. BTW, for other people come across it, it also works with object instances, e.g., class Foo { method bar($a) { dd $a } }; my $name = "bar"; say Foo.new."$name"(42); # 42. Thanks!Eternity
C
10

There are two general mechanisms for getting ahold of a method as a first class citizen (something that can be passed around as an argument), depending on whether a method is has scoped (almost all of them are) or my scoped.

.^find_method... to find a has scoped method

The vast majority of methods are held in classes and declared without an explicit scope declarator. These are all (implicitly) has scoped to the class containing them.

To pass a has scoped method around as an argument, you need to find it. The find operation must be initiated relative to some class that either contains that method or inherits from a class that contains it. This is true even if you know the method is in the very class from which you are initiating the find operation. There are three options:

  • .^find_method, as noted by ugexe++, is the usual tool. As you might expect, it searches inherited classes in Method Resolution Order, starting with the class of the .^find_method invocant.

  • On rare occasions you might want to use .^find_method_qualified instead. This starts the search higher up the MRO than the invocant's class.

  • There's one more method I'll include for completeness: .^lookup. But read the caveats in the doc. You almost certainly should NOT use this method, especially if what you wish to do is call the method. Use one of the .^find_method... methods instead.

So:

.has-way given class {
  method has-way { my $m = self.^find_method: 'm'; self.m-as-arg: $m }
  method m-as-arg ($m) { say self.$m } # has way
  method m { 'has way' }
}

&foo to refer to a my scoped method

A few methods are not has scoped but are instead declared with my.

For these lexical methods, the mechanism for passing them around as arguments is to simply refer to them with a prefix & sigil, just as you do for subs.

So:

.my-way given class {
  method my-way { self.m-as-arg: &m }
  method m-as-arg ($m) { say self.$m }     # my way
  my method m { 'my way' }
}
Conferral answered 15/7, 2019 at 21:9 Comment(1)
It's also probably not a bad idea to remind why self.$m is necessary: methods have an invocant, so just calling $m won't necessarily work as someone might think it would. (I mention this more for beginners who may see this question and not fully grasp the sub/method distinction that can not totally obvious with & sigiled variables that more often contain blocks that are more sub-like).Retribution

© 2022 - 2024 — McMap. All rights reserved.