Overview of Object Methods - Maple Programming Help

Home : Support : Online Help : Programming : Objects : object/methods

Overview of Object Methods

Methods

 • Methods are procedures defined when declaring a Maple object.  There are a few notes for defining and using object methods.

Method Names Should Be Declared static

 • In Maple, method names should be declared as static.  In most cases, all objects of the same class use the same procedures for their methods.  Thus if the method names are not declared static, each object will have a separate copy of the procedure.  For more information on using static with objects, see Creating a New Object.

Object Must Be Passed into Methods

 • If you declare a method that needs to access the contents of an object, that object must be passed into the method as an argument.  This is in contrast to some object oriented languages which allow access to the contents of an object associated with the current invocation, either through direct access to the object's members or through a self variable.  The method will have access to the local variables of any object of the same class that the method was declared in.
 • In the following example, the set and toString methods expect an instance of the StringBuffer class as their first argument.

module StringBuffer()

option object;

local buffer := Array( 1..0, datatype=integer[1] );

export set::static := proc( sb::StringBuffer, s::string, $) sb:-buffer := Array( convert( s, ':-bytes' ), datatype=integer[1] ); NULL; end; export toString::static := proc( sb::StringBuffer,$ )

convert( convert(sb:-buffer,list), ':-bytes' );

end;

export append::static := proc( sb::StringBuffer, s::string, $) local l, n; l := convert( s, ':-bytes' ); n := :-numelems( sb:-buffer )+1; sb:-buffer( n..n+:-numelems(l)-1 ) := Array(l); NULL; end; end: Calling Methods Calling an Exported Method  • To call a method that is exported by an object, use the standard function calling syntax with the object as an argument. When evaluating the function call, Maple will search the arguments, left to right, for an object that exports a method with the same name as the specified function. The first object found with a matching method will have its method called with the given arguments. > sb := Object(StringBuffer);  ${\mathrm{sb}}{≔}{\mathrm{Object<>}}$ (1) > set( sb, "Hello" ); > append( sb, " " ); > append( sb, "World" ); > toString( sb );  ${"Hello World"}$ (2) Using the function mechanism  • The standard method to invoke a method exported by an object will not work if the name of the method has a value as a global variable (unless that value has last name evaluation, for example, if it is a procedure or a module that isn't an object). Here is an example: > toString := 5;  ${\mathrm{toString}}{≔}{5}$ (3) > toString( sb );  ${5}$ (4)  • To overcome this, you can replace a call of the form method_name( arguments ) with function:-method_name( arguments ) (using the literal name function) or object:-method_name( arguments ) (using the object instance on which you would like to call the method). This works regardless of the value of the global variable. > function:-toString( sb );  ${"Hello World"}$ (5) > sb:-toString( sb );  ${"Hello World"}$ (6)  • You can find more information about the function mechanism on the Object/function_mechanism help page. Calling a local Method  • Locals can only be accessed by methods defined within the object's declaration, so normal scoping rules are usually sufficient to resolve the function call to the correct procedure. Objects in Indexed Function Calls  • When making an indexed function call (of the form func[index](args)) Maple will also check the indices (index) for a matching object as well as the arguments. If a matching object is found in the indices, that object will be used before one found in the arguments. Using ModuleCopy and ModuleApply to Create an Object Factory  • The special ModuleCopy and ModuleApply methods can be combined to implement an object factory routine. The ModuleCopy method is used to define what happens when the Object routine is called to create a new object. The ModuleApply method defines what happens when an object name is applied (that is, used as a function call).  • By adding the following methods to the declaration of StringBuffer, the ModuleApply method becomes a factory routine. export ModuleCopy::static := proc( sb::StringBuffer, proto::StringBuffer, s::{string,StringBuffer,':-list'( ':-integer[1]' )},$ )

if ( _npassed = 3 ) then

if ( s::':-string' ) then

set( sb, s );

elif ( s::':-list'( ':-integer[1]' ) ) then

sb:-buffer := Array( s, datatype=integer[1] );

NULL;

else

sb:-buffer := Array( s:-buffer );

NULL;

end;

else

sb:-buffer := Array( proto:-buffer );

NULL;

end;

end;

export ModuleApply::static := proc( )

Object( StringBuffer, _passed );

end;

 • Having defined a ModuleCopy and ModuleApply, we can now call StringBuffer like a function to create new instances.

> sb2 := StringBuffer();

 ${\mathrm{sb2}}{≔}{\mathrm{Object<>}}$ (7)

> set( sb2, "StringBuffer" );

> toString( sb2 );

 ${5}$ (8)

> sb3 := StringBuffer( "Another Buffer" );

 ${\mathrm{sb3}}{≔}{\mathrm{Object<>}}$ (9)

> toString( sb3 );

 ${5}$ (10)

> sb4 := StringBuffer( sb3 );

 ${\mathrm{sb4}}{≔}{\mathrm{Object<>}}$ (11)

> toString( sb4 );

 ${5}$ (12)
 • There are other special method names that can be defined. For a complete list of these methods, see the Overview of Objects in Maple page.

Operator Methods

 • Objects in Maple can implement operator methods.  These methods will be invoked when objects are used with the corresponding operators in Maple. One can implement the = operator for StringBuffer as follows:

export =::static := proc( s1, s2, $) if ( _npassed = 1 ) then return false; end; if ( not s1::{string,StringBuffer} or not s2::{string,StringBuffer} ) then return false; end; if ( s1::string ) then evalb(s1 = toString( s2 )); else if ( s2::string ) then evalb(toString( s1 ) = s2); elif ( s1 = s2 ) then true; else EqualEntries( s1:-buffer, s2:-buffer ); end; end; end; > sb1 := StringBuffer( "Hello" );  ${\mathrm{sb1}}{≔}{\mathrm{Object<>}}$ (13) > sb2 := StringBuffer( "Hello" );  ${\mathrm{sb2}}{≔}{\mathrm{Object<>}}$ (14) > sb3 := StringBuffer( "Goodbye" );  ${\mathrm{sb3}}{≔}{\mathrm{Object<>}}$ (15) > evalb( sb1 = sb2 );  ${\mathrm{true}}$ (16) > evalb( sb1 = sb3 );  ${\mathrm{false}}$ (17) > evalb( sb1 = 1 );  ${\mathrm{false}}$ (18)  • Note Checking for the 1 argument case handles the possibility that an object is compared to NULL. > evalb( sb1 = NULL );  ${\mathrm{false}}$ (19)  • There are many operators that can be overloaded. For a complete list, and more information about overloading operators, see the Objects Overloading Operators page. Overriding builtin Routines  • Objects can override certain Maple routines of type builtin by defining a method with the same name. For example, StringBuffer can implement a numelems member as follows. export numelemsstatic := proc( sbStringBuffer, s:string,$ )

:-numelems( sb:-buffer );

end;

 • When calling the top level numelems routine, we need to use the :- notation to differentiate from StringBuffer's numelems method.  Without this the StringBuffer numelems method would have an infinite recursion.
 • With the numelems method defined, we can pass StringBuffer objects into numelems, just as we could other Maple structures.

> numelems( [ 1, 2, 3 ] );

 ${3}$ (20)

> numelems( "Hello" );

 ${5}$ (21)

> numelems( sb );

 ${11}$ (22)
 • Not all builtin routines can be overridden.  For a complete list of routines that can be overridden and more discussion of overriding builtin routines, see Objects Overloading builtin Routines.

Examples

Here is the code from the sections above as a single declaration.

 > module StringBuffer()    option object;    local buffer := Array( 1..0, datatype=integer[1] );    export ModuleCopy::static := proc( sb::StringBuffer, proto::StringBuffer,            s::{string,StringBuffer,':-list'( ':-integer[1]' )}, $) if ( _npassed = 3 ) then if ( s::':-string' ) then set( sb, s ); elif ( s::':-list'( ':-integer[1]' ) ) then sb:-buffer := Array( s, datatype=integer[1] ); NULL; else sb:-buffer := Array( s:-buffer ); NULL; end; else sb:-buffer := Array( proto:-buffer ); NULL; end; end; export ModuleApply::static := proc( ) Object( StringBuffer, _passed ); end; export numelems::static := proc( sb::StringBuffer, s::string,$ )        :-numelems( sb:-buffer );    end;    export =::static := proc( s1, s2, $) if ( _npassed = 1 ) then return false; end; if ( not s1::{string,StringBuffer} or not s2::{string,StringBuffer} ) then return false; end; if ( s1::string ) then evalb(s1 = toString( s2 )); else if ( s2::string ) then evalb(toString( s1 ) = s2); elif ( addressof(s1) = addressof(s2) ) then true; else EqualEntries( s1:-buffer, s2:-buffer ); end; end; end; export set::static := proc( sb::StringBuffer, s::string,$ )        sb:-buffer := Array( convert( s, ':-bytes' ), datatype=integer[1] );        NULL;    end;    export toString::static := proc( sb::StringBuffer, $) convert( convert(sb:-buffer,list), ':-bytes' ); end; export append::static := proc( sb::StringBuffer, s::string,$ )        local l, n;        l := convert( s, ':-bytes' );        n := :-numelems( sb:-buffer )+1;        sb:-buffer( n..n+:-numelems(l)-1 ) := Array(l);        NULL;    end; end;