How do you create a callable variable to call a class method with arguments?

265 views Asked by At

I'm trying to create a callable variable for a class method.

class Person {
    method walk(Str $direction) {
        say "Walking $direction";
    }
}

I can create a callable variable for the method 'walk' that works as expected.

my $person = Person.new;
my $callable = $person.^find_method('walk');
$person.$callable('up'); # OUTPUT: "Walking up"

Now I want to create a callable that will call method 'walk' with the parameter 'up'.

my $callable = $person.^find_method('walk').assuming('up');
$person.$callable(); 

    # Type check failed in binding to parameter '$direction'; expected Str but got Person (Person.new)
    #   in sub __PRIMED_ANON at EVAL_7 line 1
    #   in block <unit> at <unknown file> line 1

$person.$callable (without the parentheses) gives the same error

I tried calling it 'directly', but got a different error.

$person.^find_method('walk')('up')
    # Too few positionals passed; expected 2 arguments but got 1
    #   in method walk at <unknown file> line 2
    #   in block <unit> at <unknown file> line 1

Is this possible?

1

There are 1 answers

0
codesections On BEST ANSWER

Yes, this is possible. The missing piece is that methods implicitly receive their invocant as their first positional parameter (that is, the signature of Person's walk method is really method walk(Person: Str $direction)) and receive self as their first positional argument.

This means that you need to use assuming to supply the second positional argument instead of the first. Here's how that'd look:

class Person {
    method walk(Str $direction) {
        say "Walking $direction";
    }
}
my $person = Person.new;
my $callable = $person.^find_method('walk').assuming(*, 'up');
$person.$callable();  # # OUTPUT: "Walking up"

As another option, instead of using * for the first positional parameter, you could use $person; if you did that, $callable() would return "Walking up" without needing to be invoked on a Person.