The Poly::Overload module allows to define overloaded functions and methods. The overload resolution can be based on the arguments like in C++, on user preferences, or a mix of both.

The examples below are made using the special rule file syntax, as the most of the overloaded functions are defined in the rules. But the Poly::Overload module has naturally a pure perl interface, which is briefly described at the end of this page.

Signature

The argument-based overloading, aka polymorphism, requires the description of the expected arguments in a signature. The signature is an extended perl prototype. For the sake of completeness, we give here the description of standard perl syntax too.

$
denotes a single scalar argument of an arbitrary type
;
separates the obligatory arguments (to the left) from the optional arguments (to the right)
@
as the last signature element denotes arbitrary many optional arguments of arbitrary types
CLASS
requires the argument be from the specified object CLASS (package) or derived thereof.
For object types defined in the applications the common application namespace prefix may be omitted, e.g. RationalPolytope is equivalent to Apps::polytope::RationalPolytope.
%HASH { keyword => default, ... }
allows optional keyword => value pairs as described in the specified named HASH array or in the anonymous hash. Obviously, the named hash makes more sense if you have many overloaded functions sharing the same set of optional arguments.
A function may have several option lists. They must be always the last elements in the signature.

The complete signature is a string consisting of zero, one, or more elements described above. For the better legibility, it is allowed to separate elements by white spaces and commas. An empty signature describes a function taking no arguments.

Labels

Function labels allow to have several different implementations of a function, where the choice is made according to the current user preferences. The label consists of one or more keywords (obeying the perl syntax for identifiers) chained with periods. The first keyword is called a top-level label, it must be explicitly declared somewhere in the rules. The lower levels are introduced just by using them in a function declaration.

Multiple labels assigned to a function must be separated by commas.

Defining functions and methods

The overloaded functions and methods are defined in the rulefiles using the following syntax:

function NAME (SIGNATURE) { ... }
defines a polymorphic function with argument-based overload resolution.
method NAME (SIGNATURE) { ... }
defines a polymorphic method with argument-based resolution. The first parameter, which is usually the $this object reference or the package name, must not appear in the signature, as it doesn't take part in the overload resolution (it was already involved in the method lookup by the perl interpreter.)
function LABELS : NAME { ... } method LABELS : NAME { ... }
defines a function (method) with preference-based overload resolution.
function LABELS : NAME (SIGNATURE) { ... } method LABELS : NAME (SIGNATURE) { ... }
defines a function (method) with mixed overload resolution. The argument resolution is applied first.
global_method [ LABELS : ] NAME [ (SIGNATURE) ] { ... }
defines a (possibly polymorphic) method in the current package and makes it visible to the special cross-package method search. At least one kind of overloading (signature or labels) must be present.

Overload resolution

The argument-based overload resolution is, unlike in C++, greedy. It does not sum up any proximity ratings for the arguments, but proceeds with one argument after another narrowing the candidate list in each step. As soon as all the typed (object-class) arguments are checked, the final instance is choosed based on the number of arguments. All possible ambiguities are detected in the compilation phase, therefore the run-time resolution is straightforward and quick.

More precisely, all overloaded function instances are kept in a tree. The resolution starts at the root node, looking for a special internal method in a package of the first argument (or in UNIVERSAL if it is not an object reference), using the standard perl method lookup. This internal method delivers the child node in the tree, referring to the next special method, which is looked up in the package of the second argument and so on. The arguments that are untyped in all instances are just skipped to save the time. If the method lookup fails or the total number of arguments does not match the signature in the last visited node, the resolver makes a step back to the ancestor node and repeats the method lookup starting in the parent packages of the corresponding argument. When the backtracking is exhausted without valid match, an exception is raised.

The optional keyword arguments are not considered during the resolution. As soon as all the positional (preceding) arguments are successfully proceeded, the rest of the argument list is checked against the specified hash lists; if an unknown keyword encounters, an exception is raised immediately.

The preference-based resolution is performed after the evaluation of the argument list. If there are several candidate functions with identical (up to the given number of arguments) signatures, that one will be taken, whose label was made preferred via the user settings. If there are several labels attached to a function, the latest (in chronological sense) preference setting wins. If no preference settings are active, the instance defined as first in the rules is taken.

Calling overloaded functions

The overloaded functions and methods are called exactly the same way as the normal, non-overloaded perl functions and methods. The calling site does not even need to know whether the function is overloaded or not.

The code of the overloaded function accesses the arguments in a standard fashion using the @_. The only exception concerns the keyword options: they are removed from @_ and stored into anonymous hash arrays, one per hash descriptor in the signature. The references to these hash arrays are then pushed in the same order at the end of @_.

If a keyword is described with a default value different from undef, it will always be present in the argument hash. The keywords are allowed to occur in more than one hash descriptor; in this case each argument hash will contain a copy of the original argument value.

The cross-package method search mechanism allows to define different implementations of some algorithm in different packages, where they don't see each other, and then let the resolver find the suitable instance across the package boundaries. This is accomplished by two calls. First, you call the function with the required name in the package Poly::Overload::Global, passing it all arguments except the first one (which would be, as usual, the object reference or the package name.) It returns you a code reference to a method. Then you get out the class it belongs to using the method_owner function, obtain the object of the class in whatever appropriate manner and call this code, this time with an object and the rest arguments.

The visualization interface in polymake makes heavy use of global methods, you might take a glance in Visual.pm for an example of using them.

Importing overloaded functions

Normally, the visibility of an overloaded function is constrained by the package it is defined in. A function with the same name can be independently defined in another package without any interference.

However, if the overloaded function gets imported into another package, either by the standard perl import mechanism or due to the namespace mode lookup, and then further instances are defined in the importer package, the resolver trees are merged together, so that the new instances are accessible to the original package too.

Getting reference to a overloaded function

To obtain a code reference to a suitable instance of an overloaded function or method, you can use the following helper functions:

resolve Poly::Overload 'NAME', arg, ...; resolve Poly::Overload 'PACKAGE::NAME', arg, ...;
returns a code reference or undef if no matching instance exist. The first variant looks in the current package.
$obj->can('NAME', arg, ...);
Like the standard UNIVERSAL::can finds the method reference along the inheritance path, but chooses the method instance matching the given argument list. If the argument-based resolution in some class fails, no further parent classes are considered!

Note that for labeled functions and methods the returned value will be an array created by the preference management module. To get the code references, call the function Poly::Label::get_items giving it the obtained array reference, don't try to decode this array by yourself. The code references will be sorted by the actual preference ranking.

pure perl API

For the seldom case you want to introduce overloaded functions in a pure perl source text, here are the examples how to use the module Poly::Overload directly:

use Poly::Overload; add Poly::Overload 'name', 'signature', sub { ... }; add Poly::Overload 'name', 'signature', sub : method { ... }; $l=new Poly::Label(...); add Poly::Overload $l, 'name', 'signature', sub { ... }; $l2=new Poly::Label(...); add Poly::Overload [ $l, $l2 ], 'name', 'signature', sub { ... }; add_global Poly::Overload 'name', 'signature', sub : method { ... };
If name is qualified, then the function/method is defined in the given package, else it is put in the current package.