I've worked out how to do it, by adding a custom dispatch:<.?> method:

use Test;

class C {
    method foo { 42 }
    method can($meth-name) {
        my $meths = callsame;
        if !$meths && $meth-name eq 'bar' {
            $meths = [method { 69 }, ];
            self.^add_method($meth-name, $meths[0]);
        }
        $meths;
    }
    method dispatch:<.?>(\name, |c) is raw {
        self.can(name)
            ?? self."{name}"(|c)
            !! Nil
    }
    method FALLBACK(\name, |c) {
        if self.can(name) {
            self."{name}"(|c);
        }
        else {
            die X::Method::NotFound.new( :method(name),
:typename(self.^name) );
        }
    }
}

for C {
    my $obj = .new;
    is $obj.foo, 42, 'static method - direct call';
    is $obj.?foo, 42, 'static method - safe call';
    is $obj.bar, 69, 'dynamic method - direct call';
    is $obj.?bar, 69, 'dynamic method - safe call';
    dies-ok {$obj.unknown}, 'direct call - unknown method dies';
    lives-ok {$obj.?unknown}, 'safe call - unknown method lives';
}

Based on some code I found in the core Rakudo Mu class.
Slightly awkward, but at least working.

On Tue, Oct 18, 2016 at 1:35 PM, David Warring <perl6-bugs-follo...@perl.org
> wrote:

> # New Ticket Created by  David Warring
> # Please include the string:  [perl #129907]
> # in the subject line of all future correspondence about this issue.
> # <URL: https://rt.perl.org/Ticket/Display.html?id=129907 >
>
>
> I'm adding a FALLBACK method to create method dynamically, the it receives
> both '.' and '.?' invocations, and I can't distinguish between them.
>
> This then breaks $obj.unknown vs $obj.?unknown handling in class objects.
>
> Consider:
>
> use Test;
>
> class C {
>     method foo { 42 }
>     method FALLBACK($meth-name, |c) {
>         warn "can $meth-name";
>         if $meth-name eq 'bar' {
>             self.^add_method($meth-name, method { 69 });
>             self."$meth-name"(|c);
>         }
>         else {
>             warn "dunno if this is a safe method call or not";
>             Nil;
>         }
>     }
> }
>
> for C {
>     my $obj = .new;
>     is $obj.foo, 42, 'static method - direct call';
>     is $obj.?foo, 42, 'static method - safe call';
>     is $obj.bar, 69, 'dynamic method - direct call';
>     is $obj.?bar, 69, 'dynamic method - safe call';
>     todo "can't get both of these to pass!";
>     dies-ok {$obj.unknown}, 'direct call - unknown method dies';
>     lives-ok {$obj.?unknown}, 'safe call - unknown method lives';
> }
>
> I'm receiving both the safe and unsafe 'unknown' invocations, and can't
> distinguish them.
>
> I also tried overriding the classes 'can' method:
>
> class C {
>     method foo { 42 }
>     method can($meth-name) {
>         my $meths = callsame;
>         if !$meths && $meth-name eq 'bar' {
>             $meths = [method { 69 }, ];
>             self.^add_method($meth-name, $meths[0]);
>         }
>         $meths;
>     }
>     method FALLBACK($meth-name, |c) {
>         if self.can($meth-name) {
>             self."$meth-name"(|c);
>         }
>         else {
>             warn "dunno if this is a safe method call or not";
>             Nil;
>         }
>     }
> }
>
> But the FALLBACK is still needed, and still has the same problem.
>
> Any ideas?
>

Reply via email to