Search code examples
perlmoosemultimethod

How to extend Class::Multimethods::Pure to recognise Moose Roles?


I need multemethod dispatch with Moose objects. I'm doing this with Class::Multimethods::Pure. I chose this instead of MooseX::MultiMethods because it depends on MooseX::Method::Signatures which can't install on my system because it fails its tests. I don't mind if you have an alternative approach to suggest.

The following works fine with types and subtypes:

package Foo::Type;
use Moose;

package Foo::SubType;
use Moose;
extends 'Foo::Type';

package main;
use Class::Multimethods::Pure;

multi hello => ('Foo::Type') => sub {
    my ( $foo ) = @_;
    print $foo;
};

hello( Foo::SubType->new );

But the scenario I now need to handle is where the declared type is actually a Moose Role:

package Foo::Role;
use Moose::Role;

package Foo::Type;
use Moose;
with 'Foo::Role';

package main;
use Class::Multimethods::Pure;

multi hello => ('Foo') => sub {
    my ( $foo ) = @_;
    print $foo;
};

hello( Foo::Type->new );

But this can't recognise the role:

No method found for args (Foo::Type=HASH(0x22ac854))

The documentation says it can be extended in various ways, including adding Perl 6-ish roles. But it's a little sketchy for me and I'm looking for a more detailed example. Has anyone tried this?


Solution

  • My solution was to convert the roles to abstract base classes using MooseX::ABC. In this way, they could be recognised as a class type.

    On a side note, I managed to get MooseX::MultiMethods working on another system. It does work with roles, but it can't figure out which to use if we define a multimethod that takes the class and another multimethod that takes the role. Incidentally, MooseX::ABC resolved this issue also since it gave me a hierarchical structure which the roles did not really have.

    package Foo::Role;
    use Moose::Role;
    
    package Foo::Type;
    use Moose;
    with 'Foo::Role';
    
    package Merger;
    use Moose;
    use MooseX::MultiMethods;
    
    multi method hello (Foo::Role $foo) {
        print 'Foo::Role: '.$foo;
    }
    multi method hello (Foo::Type $foo) {
        print 'Foo::Type: '.$foo;
    }
    
    package main;
    my $merger = Merger->new;
    my $f = Foo::Type->new;
    $merger->hello( $f );
    # Ambiguous match for multi method hello: (Foo::Role $foo), (Foo::Type $foo) 
    # with value [ Merger{  }, Foo::Type{  } ]