define procedures with the same name operating on different argument types

Parameters

 l - list of procedures with option overload p - procedure returned by a previous call to overload

Description

 • The overload command allows you to split the implementation of a command that operates on different argument types into separate procedures.
 • Option overload denotes a procedure that operates on only the specific argument types described in the parameter declarations. Procedures with this option are intended to be used in conjunction with the overload command, or as part of a package that is loaded using with. Under these conditions, the next available procedure is called when an argument type match fails.
 • The overload command accepts a list of procedures, l = [p1,p2,...,pn], and returns a single procedure, P, that combines all of the sub-procedures.  Calling P(x,y); will first attempt to run p1(x,y).  Execution will proceed as follows.
 If p1 computes a result, that result is returned.
 If p1 generates an error that is not prefixed by "invalid input:", then execution stops, and that exception is raised.
 If p1 raises an exception prefixed by "invalid input:", and p1 has option overload specified, then execution will proceed with p2(x,y).
 If p1 raises an exception during the parameter type-checking phase only (not inside p1), and option overload(callseq_only) has been specified, then execution will proceed to p2(x,y).
 If p1 does not have any option overload designation, then execution will always terminate with whatever p1 does.  Usually, only the last procedure in the overload list should omit option overload.
 The transition is made from p1 to p2 to p3, through to pn as long as the calling sequences do not match, or an invalid input exception is raised, and the sub-procedure has the appropriate overload option.  If no procedure call leads to a result, a generic exception is raised.
 If overload is called with the first argument p, then the given list, l, is appended to the existing list contained in p.  When the returned procedure is called, each argument signature in the embedded procedures is checked.  If no type mismatch occurs, the procedure inside l with the matching type signature is called.
 • The with command can be used to load a package containing procedures with option overload.  This will override any other commands with the same name that were previously loaded, only insofar as the new procedure's type signature declares. For example, you can define a specialized version of the + function as in some of the examples below.  Your function will only be invoked when adding expressions that match the type specified in your option overload procedure.  Otherwise the default, built-in implementation will be used.  Multiple implementations of + can be loaded simultaneously to act on different package-specific domains.
 • For a list of built-in operators that can be overloaded, see use.
 • Element-wise operators can be overloaded by binding the ~ function.  The ~ function gets called with the operation in square brackets as an index to the function name.  In order to distinguish element-wise operator calls with an expression sequence on either side of the operator, the arguments are separated by a special fence token,  $ (space-dollar sign). The statement, a +~ b is recast as ~[+](a,$,b).  Uses of tilde following a function name and argument sequence do not have the fence argument.  Alternately, element-wise operations can be overloaded by defining methods for indexing (?[]), and bounds-checking (upperbound).  See the example at the end of this page.

Examples

 This example separates the implementation of list-append and Array-append:
 > append := overload(     [         proc( l::list, e::anything ) option overload;             [ op(l), e ];         end,         proc( a::Array(1..-1), e::anything ) option overload;             a(numelems(a)+1) := e;         end     ] ):
 > $\mathrm{append}\left(\left[1,2\right],3\right)$
 $\left[{1}{,}{2}{,}{3}\right]$ (1)
 > $\mathrm{append}\left(\mathrm{Array}\left(\left[77,88\right]\right),99\right)$
 $\left[\begin{array}{ccc}{77}& {88}& {99}\end{array}\right]$ (2)
 Extend the above example with a common helper function to do additional argument checking.  In this case use option overload(callseq_only) so that invalid-input exceptions from inside the helper function are raised to the top.
 > VerifyUnique := proc( a, e )     if member(e,a) then         error "invalid input: %1 already exists in the collection",e;     end if; end proc:
 > AppendUnique := overload(     [            proc( l::list, e::anything ) option overload(callseq_only);             VerifyUnique(l,e);             [ op(l), e ];         end,         proc( a::Array(1..-1), e::anything ) option overload(callseq_only);             VerifyUnique(a,e);             a(numelems(a)+1) := e;         end     ] ):
 > $a≔\mathrm{AppendUnique}\left(\left[1,2\right],3\right)$
 ${a}{≔}\left[{1}{,}{2}{,}{3}\right]$ (3)
 > $a≔\mathrm{AppendUnique}\left(a,3\right)$
 The error raised above is expected -- the given list already contains a, so our overload is rejecting the attempt to append it again.

 In this example we will look at redefining the definition of + and - for sets to mean union and difference.
 > SetOperations := module() option package;  export +;     + := proc(a::set, b::set) option overload;         a union b;     end proc; end module:
 > $\mathrm{with}\left(\mathrm{SetOperations}\right)$
 $\left[{\mathrm{+}}\right]$ (4)
 > $\left\{1,2,3\right\}+\left\{3,4\right\}$
 $\left\{{1}{,}{2}{,}{3}{,}{4}\right\}$ (5)
 Note that the overload applies only to operations on sets because of the argument type checks in the definition of + and -. Using + with other data types does not invoke this method.
 > $1+1$
 ${2}$ (6)
 The - operator in Maple is interpreted as a unary operator.  Subtraction must be handled as the compound operation, a - b  =  +(a,-(b)).
 > SetOperations := module() option package;  export +, -;     + := overload( [         proc(a::set, b::set) option overload;         a union b;     end proc,         proc(a::set, b::MINUS(set)) option overload;             a minus op(b);         end,         proc(a::MINUS(set), b::set) option overload;             b minus op(a);         end     ]);     - := proc(a::set) option overload;         MINUS(a);     end proc; end module:
 > $\mathrm{with}\left(\mathrm{SetOperations}\right)$
 $\left[{\mathrm{+}}{,}{\mathrm{-}}\right]$ (7)
 > $\left\{1,2,3\right\}+\left\{3,4\right\}$
 $\left\{{1}{,}{2}{,}{3}{,}{4}\right\}$ (8)
 > $\left\{1,2,3\right\}-\left\{3,4\right\}$
 $\left\{{1}{,}{2}\right\}$ (9)
 > $1+1$
 ${2}$ (10)
 > $3-2$
 ${1}$ (11)

 Indexing can be overloading by defining a procedure named ?[].  For clarity we will separate the select and assignment operations in this example.  Both are dispatched to from the main ?[] procedure. Note that proper assignment requires the "var" argument to be ::uneval.  Evaluation must be done by calling eval();
 > Quaternion := module()     option package;     export ?[], *, +, -:     local ?[assign], ?[select], ModuleLoad, ModuleApply:     ModuleApply := proc( h, i, j, k )        QUATERNION(h,i,j,k);     end proc:     ModuleLoad := proc()         global print/QUATERNION;         print/QUATERNION := proc(h,i,j,k)          h + i *i + j *j + k *k;         end proc:         TypeTools[AddType]( tQuaternion, t->evalb(op(0,t) = 'QUATERNION') );     end proc:     ModuleLoad();     + := proc(a::tQuaternion, b::tQuaternion)         option overload:         QUATERNION(op(1,a)+op(1,b), op(2,a)+op(2,b),                    op(3,a)+op(3,b), op(4,a)+op(4,b));     end proc;     - := proc(a::tQuaternion)         option overload:         QUATERNION(-op(1,a), -op(2,a), -op(3,a), -op(4,a));     end proc;     * := proc(a::tQuaternion, b::tQuaternion)         option overload:         local A, B;         A := [op(a)]:  B := [op(b)]:         QUATERNION(A[1]*B[1] - A[2]*B[2] - A[3]*B[3] - A[4]*B[4],            A[1]*B[2] + A[2]*B[1] + A[3]*B[4] - A[4]*B[3],            A[1]*B[3] - A[2]*B[4] + A[3]*B[1] + A[4]*B[2],            A[1]*B[4] + A[2]*B[3] - A[3]*B[2] + A[4]*B[1]);     end proc;     ?[] := proc(q::uneval, index::[{1,2,3,4}], val)         option overload:         if nargs = 2 then             ?[select](eval(q),op(index));         else             ?[assign](q,eval(q),op(index),op(val));         end if;     end proc:     ?[select] := proc(q::tQuaternion, index::{1,2,3,4} )     op(index, q)     end proc:     ?[assign] := proc(var::uneval, q::tQuaternion, index::{1,2,3,4},                         val::integer )     var := subsop(index = val, q);     end proc: end module:
 > $\mathrm{with}\left(\mathrm{Quaternion}\right)$
 $\left[{\mathrm{*}}{,}{\mathrm{+}}{,}{\mathrm{-}}{,}{\mathrm{?\left[\right]}}\right]$ (12)
 > $a≔\mathrm{Quaternion}\left(5,6,7,8\right)$
 ${a}{≔}{5}{+}{6}{}{i}{+}{7}{}{j}{+}{8}{}{k}$ (13)
 > $b≔\mathrm{Quaternion}\left(1,2,3,4\right)$
 ${b}{≔}{1}{+}{2}{}{i}{+}{3}{}{j}{+}{4}{}{k}$ (14)
 > $c≔a+b$
 ${c}{≔}{6}{+}{8}{}{i}{+}{10}{}{j}{+}{12}{}{k}$ (15)
 > $c\left[2\right]≔7$
 ${6}{+}{7}{}{i}{+}{10}{}{j}{+}{12}{}{k}$ (16)
 > $c$
 ${6}{+}{7}{}{i}{+}{10}{}{j}{+}{12}{}{k}$ (17)
 > $ab$
 ${-}{60}{+}{20}{}{i}{+}{14}{}{j}{+}{32}{}{k}$ (18)
 > $ba$
 ${-}{60}{+}{12}{}{i}{+}{30}{}{j}{+}{24}{}{k}$ (19)

Bindings and the with Command

 When procedures with option overload are called directly, bypassing the with() bindings, or implicitly inside a package module, they behave as if they were ordinary procedures, and do not attempt to call another implementation in light of a type mismatch.  Consider the following example.
 > FloatTools := module()   option package;   export rhs, decimalpart, alt_decimalpart;   local RHS;   rhs := proc(f::float)     option overload;     f-trunc(f);   end proc:   decimalpart := proc(f)     rhs(f);   end proc;   RHS := overload( [rhs, :-rhs ] );   alt_decimalpart := proc(f)     RHS(f);   end proc; end module:
 After loading the package declared above, the 'rhs' function will work in the new way on float types, while calls to 'rhs' with other data continue to work as usual.
 > $\mathrm{with}\left(\mathrm{FloatTools}\right)$
 $\left[{\mathrm{alt_decimalpart}}{,}{\mathrm{decimalpart}}{,}{\mathrm{rhs}}\right]$ (20)
 > $\mathrm{rhs}\left(3.14\right)$
 ${0.14}$ (21)
 > $\mathrm{rhs}\left({x}^{2}-1={y}^{2}\right)$
 ${{y}}^{{2}}$ (22)
 The function 'decimalpart' is similar to 'rhs' except it effectively calls FloatTools:-rhs directly, therefore bypassing the overload mechanism.  The second call to 'decimalpart below' will raise a type-mismatch error.
 > $\mathrm{decimalpart}\left(3.14\right)$
 ${0.14}$ (23)
 > $\mathrm{decimalpart}\left({x}^{2}-1={y}^{2}\right)$
 The error above is expected because we are calling the local 'rhs' method, but we actually want the top-level implementation for equations.  To reference the 'rhs' command within the package definition in a way that will do the type matching, use the 'overload' command to define a new function, and use that new function where appropriate. The example above defines 'RHS' to use internally for this reason. The alternate implementation of 'decimalpart', called 'alt_decimalpart', does not raise an exception when given non-float input.
 > $\mathrm{alt_decimalpart}\left(3.14\right)$
 ${0.14}$ (24)
 > $\mathrm{alt_decimalpart}\left({x}^{2}-1={y}^{2}\right)$
 ${{y}}^{{2}}$ (25)

Element-wise Operations

 In this section we will look at two ways of defining how element-wise operations can work on a given object.
 In this method the object exports a procedure named ~.  All element-wise operations involving objects of this type will be dispatched through this procedure.  Since the data is a container, the implementation simply unpacks the object data from each of the arguments and then calls the global element-wise operator, :-~.
 > $\mathrm{unprotect}\left('\mathrm{Obj}'\right)$
 > module Obj() option object;   export data, ~;   data := <1,2;3,4>;   ~ := proc()        local newargs, operation, i;        operation := op(procname);        newargs := seq(if(i::thismodule,i:-data,i),i=args);        return :-~[operation](newargs);   end; end module:
 > $\mathrm{~}\left[\mathrm{+}\right]\left(\mathrm{Obj},\mathrm{ },⟨⟨1|1⟩,⟨2|2⟩⟩\right)$
 $\left[\begin{array}{cc}{2}& {3}\\ {5}& {6}\end{array}\right]$ (26)
 > $\mathrm{~}\left[\mathrm{*}\right]\left(⟨⟨10|10⟩,⟨10|10⟩⟩,\mathrm{ },\mathrm{Obj}\right)$
 $\left[\begin{array}{cc}{10}& {20}\\ {30}& {40}\end{array}\right]$ (27)
 2. Overload the index operation ?[], and the upperbound method:
 In this case the data held by the object is a list of vectors.  It is not in a format that is able to be passed to the top-level tilde command.  Rather than overload ~, and because indexing is already defined for this object via ?[], adding upperbound allows Maple to query the size of the data so it can properly perform the element-wise operations for the object.
 > $\mathrm{unprotect}\left('\mathrm{Obj2}'\right)$
 > module Obj2() option object;   export ?[], upperbound, data;   data := [<1,2>, <3,4>];   ?[] := proc(me,idx::[posint,posint])        me:-data[idx[1]][idx[2]];   end proc;   upperbound := proc(me,dim:=NULL)       if dim = NULL then           op(map(:-upperbound,me:-data));       else           :-upperbound(me:-data[dim],1);       end if;   end proc; end module:
 > $\mathrm{Obj2}\left[2,1\right]$
 ${3}$ (28)
 > $\left[\mathrm{upperbound}\left(\mathrm{Obj2}\right)\right]$
 $\left[{2}{,}{2}\right]$ (29)
 > $\mathrm{~}\left[\mathrm{+}\right]\left(\mathrm{Obj2},\mathrm{ },⟨⟨1|1⟩,⟨2|2⟩⟩\right)$
 $\left[\begin{array}{cc}{2}& {3}\\ {5}& {6}\end{array}\right]$ (30)
 > $\mathrm{~}\left[\mathrm{*}\right]\left(⟨⟨10|10⟩,⟨10|10⟩⟩,\mathrm{ },\mathrm{Obj2}\right)$
 $\left[\begin{array}{cc}{10}& {20}\\ {30}& {40}\end{array}\right]$ (31)

Compatibility

 • The callseq_only option was introduced in Maple 16.