Multimethod for Proxy

Viewed 84

Is it possible to use multidispatch for the store method when using a Proxy? In the following minimal example, the code is called when storing an Int

my $foo := do {
  my $bar = 1;
  Proxy.new:
    :FETCH( method { return $bar} ),
    :STORE( method (Int $i) { $bar = $i } )
}

say $foo;   # 1
$foo = 2;
say $foo;   # 2
$foo = "3"; # error, need to pass an Int

But I'd like to handle the STORE differently if given, say, a Str. The work around I've found (other than doing a mega method with given/where is to create a multi sub inside of a block, and return the sub (because a multi method can't be referred to with &foo) with an dummy first parameter:

my $foo := do {
  my $bar = 1;
  Proxy.new:
    :FETCH( method { return $bar} ),
    :STORE( 
      do { 
        multi sub xyzzy ($, Int $i) { $bar = $i }
        multi sub xyzzy ($, Str $i) { $bar = +$i + 1}
        &xyzzy
      }
    )
}

say $foo;   # 1
$foo = 2;   
say $foo;   # 2
$foo = "3"; 
say $foo;   # 4

Is there a better way to do this (mainly for code clarity using method because sub feels...misleading)?

1 Answers

With regards to being misleading: the FETCH and STORE values expecte Callables, which could be either a method or a sub.

Getting back to the question, there is no direct way of doing this, but there is a better indirect way that may be clearer. You can do this by setting up the multi sub first, and then passing the proto as the parameter:

proto sub store(|) {*}
multi sub store(\self, Int) { say "Int" }
multi sub store(\self, Str) { say "Str" }

my $a := Proxy.new(
  FETCH => -> $ { 42 },
  STORE => &store,
);

say $a;     # 42
$a = 42;    # Int
$a = "foo"; # Str

And if you want to make the code shorter, but possibly less understandable, you can get rid of the proto (because it will be auto-generated for you) and the sub in the multi (because you can):

multi store(\self, Int) { say "Int" }
multi store(\self, Str) { say "Str" }

my $a := Proxy.new(
  FETCH => -> $ { 42 },
  STORE => &store,
);

say $a;     # 42
$a = 42;    # Int
$a = "foo"; # Str
Related