define procedures with the same name operating on different argument types - Maple Help

overload - 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.

Examples

Example separating 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;
 ${\mathrm{VerifyUnique}}{:=}{\mathbf{proc}}\left({a}{,}{e}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{if}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{member}}{}\left({e}{,}{a}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{then}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{error}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{"invalid input: %1 already exists in the collection"}{,}{e}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end if}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end proc}}$ (3)
 > 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]$ (4)
 > $a:=\mathrm{AppendUnique}\left(a,3\right)$

Overload existing operators. Note that the overload applies only to operations on sets because of the argument type checks in the definition of + and -.

 > SetOperations := module() option package;  export +;     + := proc(a::set, b::set) option overload;         a union b;     end proc; end module;
 ${\mathrm{SetOperations}}{:=}{\mathbf{module}}\left({}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{option}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{package}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{export}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{+}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end module}}$ (5)
 > $\mathrm{with}\left(\mathrm{SetOperations}\right)$
 $\left[{\mathrm{+}}\right]$ (6)
 > $\left\{1,2,3\right\}+\left\{3,4\right\}$
 $\left\{{1}{,}{2}{,}{3}{,}{4}\right\}$ (7)
 > $1+1$
 ${2}$ (8)

The - operator is unary.  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{SetOperations}}{:=}{\mathbf{module}}\left({}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{option}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{package}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{export}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{+}}{,}{\mathrm{-}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end module}}$ (9)
 > $\mathrm{with}\left(\mathrm{SetOperations}\right)$
 $\left[{\mathrm{+}}{,}{\mathrm{-}}\right]$ (10)
 > $\left\{1,2,3\right\}+\left\{3,4\right\}$
 $\left\{{1}{,}{2}{,}{3}{,}{4}\right\}$ (11)
 > $\left\{1,2,3\right\}-\left\{3,4\right\}$
 $\left\{{1}{,}{2}\right\}$ (12)
 > $1+1$
 ${2}$ (13)
 > $3-2$
 ${1}$ (14)

An example overloading ?[].  Note that the select and assignment operations are split into separate procedures called by the main ?[] procedure for clarity.  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]$ (15)
 > $a:=\mathrm{Quaternion}\left(5,6,7,8\right)$
 ${a}{:=}{5}{+}{6}{}{i}{+}{7}{}{j}{+}{8}{}{k}$ (16)
 > $b:=\mathrm{Quaternion}\left(1,2,3,4\right)$
 ${b}{:=}{1}{+}{2}{}{i}{+}{3}{}{j}{+}{4}{}{k}$ (17)
 > $c:=a+b$
 ${c}{:=}{6}{+}{8}{}{i}{+}{10}{}{j}{+}{12}{}{k}$ (18)
 > ${c}_{2}:=7$
 ${6}{+}{7}{}{i}{+}{10}{}{j}{+}{12}{}{k}$ (19)
 > $c$
 ${6}{+}{7}{}{i}{+}{10}{}{j}{+}{12}{}{k}$ (20)
 > $ab$
 ${-}{60}{+}{20}{}{i}{+}{14}{}{j}{+}{32}{}{k}$ (21)
 > $ba$
 ${-}{60}{+}{12}{}{i}{+}{30}{}{j}{+}{24}{}{k}$ (22)

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]$ (23)
 > $\mathrm{rhs}\left(3.14\right)$
 ${0.14}$ (24)
 > $\mathrm{rhs}\left({x}^{2}-1={y}^{2}\right)$
 ${{y}}^{{2}}$ (25)

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}$ (26)
 > $\mathrm{decimalpart}\left({x}^{2}-1={y}^{2}\right)$

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}$ (27)
 > $\mathrm{alt_decimalpart}\left({x}^{2}-1={y}^{2}\right)$
 ${{y}}^{{2}}$ (28)