Class-Multimethods
view release on metacpan or search on metacpan
tutorial.html view on Meta::CPAN
</A><A HREF="http://www.csse.monash.edu.au/~damian">http://www.csse.monash.edu.au/~damian</A></FONT></H3></CENTER>
<H2>
Abstract</H2>
<FONT SIZE=-1><FONT FACE="Times">Sometimes Perl's standard polymorphic
method dispatch mechanism isn't sophisticated enough to cope with the complexities
of finding the right method to handle a given situation. For example, in
a graphical user interface, objects representing events may be passed to
objects representing windows. What happen next depends on the type of window,
but also on the type of event. It's not enough to invoke a </FONT><FONT FACE="Courier">receive_event</FONT><FONT FACE="Times">
method on the window object, since that won't distinguish between the various
possible kinds of event. Nor is it sufficient to invoke a </FONT><FONT FACE="Courier">send_to_window</FONT><FONT FACE="Times">
method on the event object, since that won't distinguish between the various
possible kinds of window. What's needed is the ability to polymorphically
select a suitable method for the appropriate combination of window and
event types. This paper describes a new CPAN module--Class::Multimethods--that
provides such a mechanism.</FONT></FONT>
<H2>
What is multiple dispatch?</H2>
<FONT SIZE=-1><FONT FACE="Times">In object-oriented Perl, the selection
of which subroutine to call in response to a method invocation (e.g. </FONT><FONT FACE="Courier">$objref->method(@args)</FONT><FONT FACE="Times">)
is performed polymorphically. That means the subroutine that's invoked
is the one defined in the class that the invoking object belongs to. So
a call such as </FONT><FONT FACE="Courier">$objref->method(@args)</FONT><FONT FACE="Times">
invokes the method </FONT><FONT FACE="Courier">CLASSNAME::method</FONT><FONT FACE="Times">,
where </FONT><FONT FACE="Courier">"CLASSNAME"</FONT><FONT FACE="Times">
is the class name returned by </FONT><FONT FACE="Courier">ref($objref)</FONT><FONT FACE="Times">.</FONT></FONT>
<P><FONT SIZE=-1><FONT FACE="Times">If the class in question doesn't have
a suitable method, then the dispatch procedure searches upwards through
the various ancestors of the original class, looking for an appropriate
subroutine. If that search fails, the dispatch procedure attempts to invoke
an </FONT><FONT FACE="Courier">AUTOLOAD</FONT><FONT FACE="Times"> subroutine
somewhere in the invoking object's inheritance hierarchy.</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>The important point is that, whichever
subroutine the method dispatcher eventually selects, it was all determined
by the class of the original invoking object (i.e. according to the class
of the first argument).</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>For most applications, the ability
to select behaviour based on the type of a single argument is sufficient.
However, some applications, such as the GUI event handler mentioned above,
need to select the most applicable polymorphic method on the basis of more
than one argument. This behaviour is known as <I>multiple dispatch</I>.</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>Generally speaking, multiple dispatch
is needed whenever two or more objects belonging to different class hierarchies
are going to interact, and we need to do different things depending on
the combination of actual types of those objects. Typical applications
that need this kind of ability include graphical user interfaces, image
processing libraries, mixed-precision numerical computation systems, and
most types of simulations.</FONT></FONT>
<P><FONT SIZE=-1><FONT FACE="Times">It's possible to build "hand-crafted"
multiply dispatched methods that look at the types of each of their arguments
and react accordingly. For example, a normal (<I>singly-dispatched</I>)
method could use </FONT><FONT FACE="Courier">ref</FONT><FONT FACE="Times">
or </FONT><FONT FACE="Courier">isa</FONT><FONT FACE="Times"> to determine
the types of its other arguments and then select the correct behaviour
in a cascaded </FONT><FONT FACE="Courier">if</FONT><FONT FACE="Times">
statement. Alternatively, it's possible to use hashes of hashes to set
up a multidimensional table of subroutine references, then use the class
names of the arguments (again found with </FONT><FONT FACE="Courier">ref</FONT><FONT FACE="Times">)
to index into it. Both these approaches are described in detail in <A HREF="#references">[1,2]</A>.</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>The problem is that such hand-crafted
mechanisms are complicated to construct and even harder to debug. And because
every hand-built method is structurally similar, they're also tedious to
code and maintain. Life would be very much easier if it were possible to
define a set of identically named methods with distinct parameter lists,
and then the program would "automagically" find the right one. Such a set
of multiply dispatched methods is known as a <I>multimethod</I>, and each
alternative method in the set is known as a <I>variant</I>.</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>But Perl has no mechanism for specifying
parameter types, or for overloading subroutine names. And certainly there's
no mechanism for automatically selecting between several (hypothetical)
overloaded subroutines on the basis of the inheritance relationships of
those (unspecifiable) parameter types.</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>Until now.</FONT></FONT>
<H2>
The Class::Multimethods module</H2>
<FONT SIZE=-1><FONT FACE="Times">The Class:Multimethod module<A HREF="#references">[3]</A>
exports a subroutine (named </FONT><FONT FACE="Courier">multimethod</FONT><FONT FACE="Times">)
that can be used to declare other subroutines that are to be multiply dispatched.</FONT></FONT>
<P><FONT SIZE=-1><FONT FACE="Times">The new dispatch mechanism looks at
the classes or types of each argument to the multimethod (by calling </FONT><FONT FACE="Courier">ref</FONT><FONT FACE="Times">
on each) and determines the "closest" matching variant of the multimethod,
according to the parameter types specified in the variants' definitions
(see below for a definition of "closest").</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>The result is something akin to C++'s
function overloading but more sophisticated, since multimethods take the
inheritance relationships of each argument into account. Another way of
thinking of the mechanism is that it performs polymorphic dispatch on every
argument of a method, not just on the first.</FONT></FONT>
<H2>
Defining multimethods</H2>
<FONT SIZE=-1><FONT FACE="Courier">Class::Multimethods::multimethod</FONT><FONT FACE="Times">
can be used to specify multimethod variants with the dispatch behaviour
described above. It takes the name of the desired multimethod, a list of
class names, and a subroutine reference, and uses this information to generate
a corresponding multimethod variant within the current package.</FONT></FONT>
<P><FONT FACE="Times"><FONT SIZE=-1>For example:</FONT></FONT>
<PRE>package LargeInt; @ISA = (LargeNum);
package LargeFloat; @ISA = (LargeNum);
package LargeNum;
use Class::Multimethods;
multimethod
divide => (LargeInt, LargeInt) =>
sub {
LargeInt::divide($_[0],$_[1])
};
multimethod
( run in 0.583 second using v1.01-cache-2.11-cpan-140bd7fdf52 )