Objects Overloading builtin Routines
Overloading builtin Routines
Special Considerations
Overriding Particular Routines
An object may override a routine by exporting a method of the same name. Any routine implemented in Maple code can be overridden. However, not all routines of type builtin can be overridden. This page discusses how to override those builtin routines. Operators that can be overridden are discussed on the page object,operators.
Overridable builtin Routines
The following builtin functions can be overridden by object methods:
abs
anames
andmap
conjugate
convert
diff
entries
eval
evalf
evalhf
expand
has
hastype
Im
indets
indices
length
lowerbound
map, map2, map[n]
max
member
membertype
min
normal
numboccur
numelems
ormap
Re
remove
select
selectremove
sort
subs
subsindets
trunc
type
upperbound
xormap
If an object implements a method to override a builtin routine, that method is still a procedure that follows the normal binding rules within the object declaration. This means that Maple may call the override even when the programmer wanted to call the top-level routine. To make sure that a function call invokes the top-level routine, use the fully qualified name. (Even when using the fully qualified name, if any of the arguments are objects exporting that name, the override will typically be called from within the builtin function.)
Special considerations for overriding particular operators are described in the object,operators help page.
andmap, ormap, and xormap
The andmap (and ormap and xormap) method expects two or more arguments:
export andmap::static := proc( p::appliable, self ) ... end proc:
The p argument is the procedure to be applied to each element of the object, and is expected to return true, false, or FAIL. The method should check the validity of the return value. When p returns false in andmap, the method should immediately return false. Likewise, when p returns true in ormap, the method should immediately return true.
If a container object does not export andmap (or ormap or xormap), and one of these functions is called with such an object as the second argument, Maple will attempt to use either the object's ModuleIterator method if it exists, or its lowerbound, upperbound, and ?[] methods to iterate over the object, calling p on the object's behalf, just as if the function had been called on a built-in container (such as a list).
A convert method needs to be able to deal with three possibilities: converting to the object from another type, converting from an object to another type, and applying a conversion where the object is an extra parameter (the third or later argument).
A diff method needs to handle three possibilities: differentiating an object, differentiating with respect to an object, or both.
If an object exports a diff method, it should also export a has method. Maple uses has to determine if an expression contains the variable of differentiation. If has returns false, 0 is returned immediately. The built-in version of has does not look inside of objects, and thus will return false.
entries and indices
The entries and indices methods should return a sequence of the entries and indices of a container object respectively. There should be a one-to-one correspondence between the indices and the entries, that is, the i-th index should yield the i-th element when passed to the ?[] method.
Each element of the sequence must be a list containing the actual entry or index. The nolist option of these builtins is handled automatically by Maple, and need not be considered when implementing these methods.
In order for the pair option of indices and/or entries to work with an object, both methods must be implemented. Again, Maple itself handles this option; the methods need not consider it.
The indexorder option of entries and indices is not supported.
There are two calling sequences for the built-in eval.
eval( expr );
eval( expr, substitutions );
An object's eval method only overrides the second calling sequence.
As eval works recursively, if expr contains an object that implements eval, that object's eval method will be called.
In the second calling sequence of the built-in eval, the substitutions can be specified in various ways. The eval method will have each equation passed as a separate argument.
An evalf method should convert the object into a floating-point value.
The evalf method is often called with an index (evalf[digits]) to specify the number of digits the expression should be evaluated to. When the object method is called, the value of the Digits environment variable will have been set if a value was given.
Implementing an evalhf method allows an object to be used in Maple's evalhf subsystem. An evalhf method should convert the object into one of the types that evalhf can manipulate. For the best performance, one of the following should be returned:
A hardware float
An hfarray
A Maple procedure that does not use lexical scoping
The has method checks to see if an object contains an expression, if an expression contains an object, or if one object is contained in another.
When declaring an object that exports a has method that uses the object name as a type, the named object syntax
module Obj() ... end module:
should be used. Using the assignment syntax
Obj := module() ... end module:
will cause an error to be raised during the assignment if the has method uses Obj as a type. The has routine is invoked when a top-level assignment occurs, to check if the name being assigned to is contained in the value being assigned to it. This is an attempt to avoid recursive assignments. The has method will be called for this assignment, but as the object's name has not been assigned, the type checks will fail.
lowerbound, upperbound, and ?[]
These three can be used together to implement iteration over an object, as an alternative to implementing a ModuleIterator procedure. See ModuleIterator for more details, and object,operators for special considerations when implementing `?[]`.
map, map2, and map[n]
The map, map2 and map[n] functions are all overridden by a single map method. The calling sequence of the map method is:
export map::static := proc( oindex::integer, inplace::truefalse, func ) ... end proc:
oindex: corresponds to the index of the expression to be mapped over.
inplace: determines if the map should occur in-place. If in-place mapping is not supported, then an exception should be raised if inplace is true.
func: the function to be mapped.
If the parameter declaration given above is used, then the special sequence _rest are the arguments to be passed to func and the element _rest[oindex] is the element to be mapped over. If e is an element of _rest[oindex] then the mapped value should be:
func( _rest[..oindex-1], e, _rest[oindex+1..] )
max and min
To override the max and min routines, export methods with the following declaration:
export max::static := proc( definedOnly::truefalse, a, b:=NULL, $ ) ... end proc;
export min::static := proc( definedOnly::truefalse, a, b:=NULL, $ ) ... end proc;
definedOnly: corresponds to max['defined']( .. ) and determines the behavior when undefined values are compared. When definedOnly is true, undefined values should be ignored (that is, a defined value should be returned). When definedOnly is false, the routines should return FAIL when an undefined value is encountered.
In Maple the max and min routines work on a combination of containers and elements. To handle these cases, the corresponding methods must be able to handle the case where b is NULL, indicating that it should return the maximum (or minimum) value stored in container object a, or a itself if it is not a container. In the general case, a and b will have a value and at least one of a or b will be an object. If the object is a container, then the maximum (or minimum) value between the elements in the container and the other argument should be returned. Otherwise the maximum (or minimum) value of a and b should be returned.
If a container object is empty, the max or min method should return NULL, not -infinity or infinity, as the container may be nested in other containers having values that are not numeric (e.g. strings). Maple will convert the final value of the initial call of max or min into the appropriate infinity if no values were found at all in any of the arguments.
Likewise, if a container object contains only empty sub-containers (either similar objects, or ordinary lists, sets, or Arrays), the result should be NULL as well. This is best achieved using the nodefault index option when recursively calling max or min on these sub-containers.
If an object does not define min or max, Maple will assume it represents a value instead of a container, and will attempt to determine the correct element using an overloaded < (less than) operator, if it is defined.
The member method determines if a container contains a value. If the container does contain the value, member should return the index that can be used by ?[] to retrieve that element. If the container does not contain the value, 0 should returned.
If an object wants to implement member but does not implement the ?[] method, it should return undefined if the value is in the container, and 0 if it is not.
More information on the consequences of implementing ?[] can be found in object,operators.
Evaluating an expression of the form a∈b can result in a call to member. More details can be found in object,operators.
The normal method has the following signature:
export normal::static := proc( self, { expanded::truefalse = false }, $ ) ... end proc:
expanded: true or false depending on if the expanded option was given to normal.
select, remove, and selectremove
The select and remove methods have the following signature:
export select::static := proc( inplace::truefalse, fn, self )
export remove::static := proc( inplace::truefalse, fn, self )
export selectremove::static := proc( inplace::truefalse, fn, self )
inplace: determines that the result (the first result in the case of selectremove) should be computed in-place. If the object cannot support in-place selection or removal, an exception should be raised.
Any additional arguments to select, remove, or selectremove will be passed as additional arguments to the corresponding method, and can be accessed via _rest.
The sort method has the following signature:
export sort :: static := proc( inplace::truefalse, self ) ... end proc:
inplace: determines if the sorting should be done in-place if possible.
Any additional arguments to sort will be passed as additional arguments to the sort method, and can be accessed via _rest.
The subs method has the following signature:
export subs :: static := proc( inplace::truefalse, eqns::{list,MyObject}, expr::anything, $ ) ... end proc:
inplace: determines if the substitutions should happen in-place if possible.
eqns: either a list of substitutions or an object whose subs method was invoked.
If eqns is a list, then the third argument (expr) is the object whose subs method was invoked. If the second argument is an object with a subs method, then the third parameter can be anything.
Objects can be used directly as types and an Object can define a ModuleType method to further refine this test. However, this does not allow an object to be tested by types that are not aware of the object. You can accomplish this by overriding the type method.
When the first argument of a call to type is an object that exports a type method and the second argument is not a module type and not the name `module`, then the type override will be called.
export type :: static := proc( self::MyObject, t, $ ) :-type( self:-value, t ) end proc:
The first argument is the object, the second argument is the given type.
The type override should return one of three values:
true : the object does match the given type
false : the object does not match the given type
NULL : undecided, the type checking continues as if the type override had not been written.
See Also
module
Object
object
object,create
object,method
object,operators
Download Help Document