overload
define procedures with the same name operating on different argument types
Calling Sequence
Parameters
Description
Thread Safety
Examples
Compatibility
overload(l)
overload(p, l)
proc(...) option overload; ... end proc
proc(...) option overload(callseq_only); ... end proc
l
-
list of procedures with option overload
p
procedure returned by a previous call to overload
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.
The overload command is thread-safe as of Maple 15.
For more information on thread safety, see index/threadsafe.
Using overload to Implement Polymorphism
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 ] ):
append⁡1,2,3
1,2,3
append⁡Array⁡77,88,99
778899
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≔AppendUnique⁡1,2,3
a≔1,2,3
a≔AppendUnique⁡a,3
Error, (in VerifyUnique) invalid input: 3 already exists in the collection
The error raised above is expected -- the given list already contains `a`, so our overload is rejecting the attempt to append it again.
Overloading Built-in Operators
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:
with⁡SetOperations
`+`
1,2,3+3,4
1,2,3,4
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
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:
`+`,`-`
1,2,3−3,4
1,2
3−2
1
Overloading Indexing
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:
with⁡Quaternion
`*`,`+`,`-`,?[]
a≔Quaternion⁡5,6,7,8
a≔5+6⁢i +7⁢j +8⁢k
b≔Quaternion⁡1,2,3,4
b≔1+2⁢i +3⁢j +4⁢k
c≔a+b
c≔6+8⁢i +10⁢j +12⁢k
c2≔7
6+7⁢i +10⁢j +12⁢k
c
a⁢b
−60+20⁢i +14⁢j +32⁢k
b⁢a
−60+12⁢i +30⁢j +24⁢k
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.
with⁡FloatTools
alt_decimalpart,decimalpart,rhs
rhs⁡3.14
0.14
rhs⁡x2−1=y2
y2
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.
decimalpart⁡3.14
decimalpart⁡x2−1=y2
Error, (in decimalpart) invalid input: rhs expects its 1st argument, f, to be of type float, but received x^2-1 = y^2
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.
alt_decimalpart⁡3.14
alt_decimalpart⁡x2−1=y2
Element-wise Operations
In this section we will look at two ways of defining how element-wise operations can work on a given object.
1. Overload the ~ operator:
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, :-`~`.
unprotect⁡Obj
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:
`~``+`⁡Obj,` $`,1|1,2|2
2356
`~``*`⁡10|10,10|10,` $`,Obj
10203040
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.
unprotect⁡Obj2
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:
Obj22,1
3
upperbound⁡Obj2
2,2
`~``+`⁡Obj2,` $`,1|1,2|2
`~``*`⁡10|10,10|10,` $`,Obj2
The callseq_only option was introduced in Maple 16.
For more information on Maple 16 changes, see Updates in Maple 16.
See Also
module
option
procedure
use
Download Help Document