Zydeco::Manual::05_Multimethods - multi methods
package MyApp { use Zydeco; class JSON::Encoder { multi method stringify (Undef $value) { 'null'; } multi method stringify (ScalarRef[Bool] $value) { $$value ? 'true' : 'false'; } multi method stringify (Num $value) { $value; } multi method stringify :alias(quote_str) (Str $value) { sprintf(q<"%s">, quotemeta $value); } multi method stringify (ArrayRef $arr) { sprintf( q<[%s]>, join(q<,>, map($self->stringify($_), @$arr)) ); } multi method stringify (HashRef $hash) { sprintf( q<{%s}>, join( q<,>, map sprintf( q<%s:%s>, $self->quote_str($_), $self->stringify($hash->{$_}), ), sort keys %$hash ), ); } } }
It is pretty common within a method to accept two different kinds of inputs and process them in different ways depending on their type.
method foobar ( Str|ArrayRef $x ) { if (is_Str $x) { ...; } else { ...; } }
The multi method keyword can make this a little more concise and readable.
multi method
multi method foobar ( Str $x ) { ...; } multi method foobar ( ArrayRef $x ) { ...; }
Multimethods can have different numbers of arguments, named arguments, etc.
multi method print_name () { print $self->name, "\n"; } multi method print_name ( FileHandle $fh ) { print {$fh} $self->name, "\n"; } multi method print_name ( Str $format, $fh= \*STDOUT ) { printf {$arg->fh} $format, $self->name; } $person->print_name; $person->print_name( \*STDERR ); $person->print_name( format => "NAME: %\n" );
It is possible for child classes to add additional "candidates" to a multimethod.
package MyApp { use Zydeco; class Foo { multi method foobar ( Str $x ) { ...; } } class Bar extends Foo { multi method foobar ( ArrayRef $x ) { ...; } } }
The method "foobar" on objects of the "Foo" class will only accept strings. Calling it on a "Bar" object will allow arrayrefs or strings.
Multimethods can be defined in roles and will compose together into the classes that consume them.
package MyApp { use Zydeco; role Foo { multi method foobar ( Str $x ) { ...; } } role Bar { multi method foobar ( HashRef $x ) { ...; } multi method foobar ( ArrayRef $x ) { ...; } } class Foo::Bar with Foo, Bar { multi method foobar ( ArrayRef $x ) { ...; } } }
The "foobar" method in "Foo::Bar" will accept strings, arrayrefs, and hashrefs. Its own implementation for arrayrefs will override the one found in the "Bar" role.
Sometimes multiple candidates will match the given parameters.
multi method foobar ( Num $x ) { ...; } multi method foobar ( Int $x ) { ...; } $object->foobar(123);
The number 123 is both an integer and a number, so which one "wins"? Int wins because it's a more specific type constraint.
Int
In general:
More specific type constraints beat more general type constraints.
Candidates in subclasses beat candidates inherited from parent classes.
Candidates defined directly in a class beat those imported from roles.
Candidates declared earlier beat candidates declared later.
For all the gory details, see Sub::MultiMethod.
Yes, multi and factory can be used together.
multi
factory
package MyApp { use Zydeco; class Person { has name, age; multi factory new_person ( Str $name ) { $class->new( name => $name ); } multi factory new_person ( Str $name, Num $age ) { $class->new( name => $name, age => $age ); } multi factory new_person ( HashRef $args ) { $class->new( %$args ); } } } my $alice = MyApp->new_person("Alice"); my $bob = MyApp->new_person("Bob", 49); my $carol = MyApp->new_person({ name => "Carol" });
Internally this works by creating a multi method called "__multi_factory_new_person" and creating a factory method which calls that.
The via syntax isn't supported with multi factories.
via
The usual abbreviated syntax will work for multimethods.
multi method stringify (Undef $value) = 'null';
In this chapter, we looked at the following keyword:
We have looked at four ways to define methods: method, factory, multi method, and multi factory. Now let's look at how to modify existing methods.
method
multi factory
Zydeco::Manual::06_MethodModifiers - Method modifiers to easily wrap or override inherited methods
Toby Inkster <tobyink@cpan.org>.
This software is copyright (c) 2020 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
To install Zydeco, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Zydeco
CPAN shell
perl -MCPAN -e shell install Zydeco
For more information on module installation, please visit the detailed CPAN module installation guide.