Extension Mechanisms - Maple Programming Help

Extension Mechanisms

Introduction

 Many built-in and library procedures admit user-defined extensions that increase the knowledge of these routines. For example, new types and conversions can be added dynamically to the system, and the range of mathematical functions that can be handled by evalf and diff can be expanded beyond what is provided with the standard Maple library.
 Each unique extension of a facility, such as diff or type, is associated with a name. What the name represents depends on the facility being extended. In the case of type, it is the name of the type. In the case of diff, it is the name of the function to be differentiated.
 There are two extension mechanisms in use: a classical extension mechanism used by most extensible facilities, and a modern extension mechanism, used by newer facilities that admit user extensions.
 - The classical extension mechanism has a single, global namespace for extension names.
 - The modern mechanism allows you to name extensions in multiple namespaces. (In other words, using the modern extension mechanism allows extensions to have names that are local to a procedure or module.)

Classical Extension Mechanism

 All built-in procedures and most library procedures, which admit user extensions, make use of the classical extension mechanism, based on name concatenation. Briefly, an extensible procedure extendme is given new knowledge by defining an expression (most often a procedure) assigned to a name of the form extendme/foo, where foo is the name of the extension. For instance, a new type foo can be introduced by defining either a structured type, or a procedure implementing the desired type test, assigned to the name type/foo. The Maple floating-point evaluator can be taught to evaluate a function F numerically by defining an extension named evalf/F.
 The classical extension mechanism for any given facility has a single, global namespace, because the name of the implementation of the extension is formed by concatenating two names. (For instance, evalf/F is formed as the concatenation of evalf/ and F.) Therefore, it is not possible to define a local extension with a name of the form extendme/foo. Nor is it possible to implement an extension of extendme with two distinct extensions foo, at least one of which is (necessarily) local.
 The specific rules that must be satisfied by an extension for any given facility extendme are documented in the help for that facility. For example, the specific rules that must be satisfied by an extension for the diff, type, expand, and evalf facilities are found in the diff, type, expand, and evalf,details help pages.

Example Using the expand Procedure

 An example in which the expand procedure is extended with knowledge of a new mathematical function GroupOrder (representing the order of a finite group) is as follows. It is assumed here that the names DirectProduct and QuotientGroup are inert representations of these constructions.
 > expand/GroupOrder := proc( p )     if nargs = 1 and type( p, 'specfunc( anything, DirectProduct )' ) then         mul( GroupOrder( op( i, p ) ), i = 1 .. nops( p ) )     elif nargs = 1 and type( p, 'QuotientGroup( anything, anything )' ) then         GroupOrder( op( 1, p ) ) / GroupOrder( op( 2, p ) )     else         'GroupOrder'( args )     end if end proc:
 > expand( GroupOrder( DirectProduct( A, B, C ) ) );
 ${\mathrm{GroupOrder}}{}\left({A}\right){}{\mathrm{GroupOrder}}{}\left({B}\right){}{\mathrm{GroupOrder}}{}\left({C}\right)$ (1)
 > expand( GroupOrder( QuotientGroup( G, N ) ) );
 $\frac{{\mathrm{GroupOrder}}{}\left({G}\right)}{{\mathrm{GroupOrder}}{}\left({N}\right)}$ (2)
 The mere existence of the procedure expand/GroupOrder causes the procedure expand to call it when confronted with an unevaluated call to GroupOrder.
 In the case of expand, the extension protocol specifies that the extension procedure, here expand/GroupOrder, be passed the arguments of the call to GroupOrder. In other cases (for instance, combine), the entire expression to be operated upon can be passed.

Modern Extension Mechanism

 With the introduction of modules, it is now possible to program extensible procedures and packages in a namespace-aware way. That is, it is possible to define, for example, types that are local to a module. The built-in procedure type is one example of a user-extensible procedure that has been made namespace-aware. The TypeTools package allows you to define new types in such a way that local names can be type names. (For backward compatibility, type extensions may be defined using either the classical or modern extension mechanism.) An example of a library facility that makes use of the modern extension mechanism is the verify command.
 The modern extension mechanism is normally associated with a package or other module that provides a standard API for adding, removing and querying extensions of the associated facility. Normally, at least three routines are provided: AddXXX, RemoveXXX, GetXXX. The XXX portion of the names depends on the facility being extended. For example, the TypeTools package provides three exports AddType, RemoveType and GetType, that allow you to add a new type, remove an existing type, and query the value of an existing type. Additional procedures may be provided, as appropriate.
 Because the modern extension mechanism stores each extension associated with the actual extension name (normally in a table), you can in fact define extensions that are local to a procedure or module; that is, the extension name need not be global, and distinct extensions whose name are instances of the same global name in a different namespace can coexist.
 > F := proc( s )     local  integer; # name of a local type     TypeTools:-AddType( 'integer', proc( expr )              type( expr, 'INTEGER( string )' )                   and StringTools:-IsDigit( op( 1, expr ) )     end proc );     # Test both the local and global integer' types     type( s, 'integer' ), type( s, ':-integer' ) end proc:
 > F( 2 );
 ${\mathrm{false}}{,}{\mathrm{true}}$ (3)
 > F( 'INTEGER'( "34" ) );
 ${\mathrm{true}}{,}{\mathrm{false}}$ (4)
 Note that the local type integer does not interfere with the global type integer.
 > type( 2, 'integer' );
 ${\mathrm{true}}$ (5)
 > type( 'INTEGER'( "34" ), 'integer' );
 ${\mathrm{false}}$ (6)
 (Indeed, in this case, the type persists, but there is no way to refer to the local type integer once the procedure F has returned. Moreover, each invocation of F creates a new local type integer. You can use TypeTools[RemoveType] to clean up local types that are no longer accessible.)
 > TypeTools:-GetTypes(); # one for each call to F()
 $\left[{\mathrm{su3index}}{,}{\mathrm{VectorOperatorApplied}}{,}{\mathrm{DGinvmetric}}{,}{\mathrm{c_symbol}}{,}{\mathrm{QuantumOperatorFunction}}{,}{\mathrm{System}}{,}{\mathrm{CommutativeMapping}}{,}{\mathrm{Polynomial}}{,}{\mathrm{TypesettingStructureNeedsMfence}}{,}{\mathrm{DGbftv}}{,}{\mathrm{CompositeDifferentiationOperator}}{,}{\mathrm{TensorInternalRepresentation}}{,}{\mathrm{Physics}}{:-}{\mathrm{Vectors}}{:-}{\mathrm{+}}{,}{\mathrm{noncommutative}}{,}{\mathrm{TensorWithAbstractIndices}}{,}{\mathrm{commutative}}{,}{\mathrm{Covariant}}{,}{\mathrm{Physics}}{:-}{\mathrm{d_}}{,}{\mathrm{Physics}}{:-}{\mathrm{*}}{,}{\mathrm{ExtendedDifferentialOperator}}{,}{\mathrm{nc_var_local}}{,}{\mathrm{DGinhomogeneousform}}{,}{\mathrm{SpacetimeMetric}}{,}{\mathrm{DifferentialOperatorSymbol}}{,}{\mathrm{GenericMatrix}}{,}{\mathrm{DGobject}}{,}{\mathrm{Vectorial}}{,}{\mathrm{CreationOperator}}{,}{\mathrm{TensorVector}}{,}{\mathrm{FeynmanDiagramsTensors}}{,}{\mathrm{Name}}{,}{\mathrm{ExtendedDifferentialOperatorSymbol}}{,}{\mathrm{ExtendedProjectorKind}}{,}{\mathrm{DGvector}}{,}{\mathrm{DGconnection}}{,}{\mathrm{ac_var_local}}{,}{\mathrm{ProjectorKindDot}}{,}{\mathrm{TensorIndex}}{,}{\mathrm{Physics}}{:-}{\mathrm{.}}{,}{\mathrm{EuclideanIndex}}{,}{\mathrm{ProjectorSum}}{,}{\mathrm{QuantumNumber}}{,}{\mathrm{PrettyPrintedBra}}{,}{\mathrm{BosonAnnihilationOperator}}{,}{\mathrm{ac_symbol}}{,}{\mathrm{ExtendedMatrix}}{,}{\mathrm{DGform}}{,}{\mathrm{Physics}}{:-}{\mathrm{Bracket}}{,}{\mathrm{ExtendedBra}}{,}{\mathrm{ExtendedQuantumOperator}}{,}{\mathrm{DifferentialOperator}}{,}{\mathrm{ExpandableProduct}}{,}{\mathrm{VectorOperator}}{,}{\mathrm{FermionCreationOperator}}{,}{\mathrm{AnnihilationOperator}}{,}{\mathrm{spinorindex}}{,}{\mathrm{anticommutative}}{,}{\mathrm{PrettyPrintedKet}}{,}{\mathrm{Physics}}{:-}{\mathrm{Projector}}{,}{\mathrm{TensorWithIndices}}{,}{\mathrm{Physicsd_2}}{,}{\mathrm{c_var_local}}{,}{\mathrm{c_symbol_local}}{,}{\mathrm{NegativeExponent}}{,}{\mathrm{LiteralSubscript}}{,}{\mathrm{NeedsMfence}}{,}{\mathrm{LinearOperatorFunction}}{,}{\mathrm{spaceindex}}{,}{\mathrm{PhysicsFunction}}{,}{\mathrm{Tensor}}{,}{\mathrm{BosonAnnihilationCreationOperator}}{,}{\mathrm{gaugeindex}}{,}{\mathrm{DGbiform}}{,}{\mathrm{SpacetimeIndexValue}}{,}{\mathrm{DGinhomogeneoustensor}}{,}{\mathrm{tetradindex}}{,}{\mathrm{su2matrixindex}}{,}{\mathrm{IdentityMatrix}}{,}{\mathrm{X_mu}}{,}{\mathrm{VectorDifferentialOperator}}{,}{\mathrm{Procedure}}{,}{\mathrm{PhysicsKnownFunction}}{,}{\mathrm{HalfInteger}}{,}{\mathrm{PhysicsD3_2}}{,}{\mathrm{TensorWithNumericalIndices}}{,}{\mathrm{NonCommutativeProduct}}{,}{\mathrm{QuantumOperator}}{,}{\mathrm{TensorAllowWithoutIndices}}{,}{\mathrm{AppliedQuantumOperator}}{,}{\mathrm{PhysicsVectors}}{,}{\mathrm{parameter}}{,}{\mathrm{BosonCreationOperator}}{,}{\mathrm{DGLiealgebraWithCoeff}}{,}{\mathrm{spacetimeindex}}{,}{\mathrm{ExtendedDifferentialOperatorIndexed}}{,}{\mathrm{DGscalar}}{,}{\mathrm{FermionAnnihilationOperator}}{,}{\mathrm{Physics}}{:-}{\mathrm{Vectors}}{:-}{\mathrm{.}}{,}{\mathrm{AnnihilationCreationOperator}}{,}{\mathrm{Physics}}{:-}{\mathrm{Bra}}{,}{\mathrm{nc_symbol}}{,}{\mathrm{DGdiffeq}}{,}{\mathrm{AtProcedure}}{,}{\mathrm{ProjectorKindStar}}{,}{\mathrm{DGLiealgebra}}{,}{\mathrm{UnitaryOperator}}{,}{\mathrm{ExtendedKet}}{,}{\mathrm{ProjectorKind}}{,}{\mathrm{c_var}}{,}{\mathrm{su3matrixindex}}{,}{\mathrm{dg_dg}}{,}{\mathrm{EntirelyNotNestedIntegral}}{,}{\mathrm{SubSystem}}{,}{\mathrm{ExtendedProjector}}{,}{\mathrm{VectorDifferentialOperatorCreated}}{,}{\mathrm{DGmoving_frame}}{,}{\mathrm{TensorInCurrentCoordinates}}{,}{\mathrm{DGtensor}}{,}{\mathrm{Contravariant}}{,}{\mathrm{SpaceIndexValue}}{,}{\mathrm{ProjectorCore}}{,}{\mathrm{name}}{,}{\mathrm{FeynmanDiagramsTensorsIndexed}}{,}{\mathrm{integer}}{,}{\mathrm{DGalgebra}}{,}{\mathrm{DiracSpinor}}{,}{\mathrm{ExtendedVectorDifferentialOperator}}{,}{\mathrm{DGrepresentation}}{,}{\mathrm{integer}}{,}{\mathrm{DGmultivector}}{,}{\mathrm{Physics}}{:-}{\mathrm{Ket}}{,}{\mathrm{DGtransformation}}{,}{\mathrm{PhysicsTensor}}{,}{\mathrm{PhysicsD_2}}{,}{\mathrm{DGmetric}}{,}{\mathrm{InertSymbol}}{,}{\mathrm{nc_symbol_local}}{,}{\mathrm{NumericalIndex}}{,}{\mathrm{ac_var}}{,}{\mathrm{TensorQuantumOperator}}{,}{\mathrm{Physics}}{:-}{\mathrm{D_}}{,}{\mathrm{Physics}}{:-}{\mathrm{^}}{,}{\mathrm{PhysicsMatrixWithMatrixIndices}}{,}{\mathrm{LeviCivitaTensor}}{,}{\mathrm{su2index}}{,}{\mathrm{Procedure}}{,}{\mathrm{QuantumObject}}{,}{\mathrm{ExtendedMatrixRequiringEval}}{,}{\mathrm{x_mu}}{,}{\mathrm{TensorStructure}}{,}{\mathrm{HermitianOperator}}{,}{\mathrm{ExtendedTensor}}{,}{\mathrm{ProjectorInt}}{,}{\mathrm{FermionAnnihilationCreationOperator}}{,}{\mathrm{NotNestedIntegral}}{,}{\mathrm{DifferentialOperatorIndexed}}{,}{\mathrm{ac_symbol_local}}{,}{\mathrm{SpaceTimeVectorApplied}}{,}{\mathrm{AppliableProcedure}}{,}{\mathrm{DGframemame}}{,}{\mathrm{nc_var}}\right]$ (7)
 > convert( (7), 'set' ); # these are distinct locals
 $\left\{{\mathrm{Physics}}{:-}{\mathrm{*}}{,}{\mathrm{Physics}}{:-}{\mathrm{Vectors}}{:-}{\mathrm{+}}{,}{\mathrm{Physics}}{:-}{\mathrm{Vectors}}{:-}{\mathrm{.}}{,}{\mathrm{Physics}}{:-}{\mathrm{.}}{,}{\mathrm{Physics}}{:-}{\mathrm{Bra}}{,}{\mathrm{Physics}}{:-}{\mathrm{Bracket}}{,}{\mathrm{DGbftv}}{,}{\mathrm{DGform}}{,}{\mathrm{Physics}}{:-}{\mathrm{D_}}{,}{\mathrm{Physics}}{:-}{\mathrm{Ket}}{,}{\mathrm{Name}}{,}{\mathrm{System}}{,}{\mathrm{Tensor}}{,}{\mathrm{X_mu}}{,}{\mathrm{Physics}}{:-}{\mathrm{^}}{,}{\mathrm{ac_var}}{,}{\mathrm{c_var}}{,}{\mathrm{Physics}}{:-}{\mathrm{d_}}{,}{\mathrm{dg_dg}}{,}{\mathrm{integer}}{,}{\mathrm{integer}}{,}{\mathrm{name}}{,}{\mathrm{nc_var}}{,}{\mathrm{x_mu}}{,}{\mathrm{AtProcedure}}{,}{\mathrm{Contravariant}}{,}{\mathrm{Covariant}}{,}{\mathrm{DGLiealgebra}}{,}{\mathrm{DGalgebra}}{,}{\mathrm{DGbiform}}{,}{\mathrm{DGconnection}}{,}{\mathrm{DGdiffeq}}{,}{\mathrm{DGframemame}}{,}{\mathrm{DGinvmetric}}{,}{\mathrm{DGmetric}}{,}{\mathrm{DGmoving_frame}}{,}{\mathrm{DGmultivector}}{,}{\mathrm{DGobject}}{,}{\mathrm{DGscalar}}{,}{\mathrm{DGtensor}}{,}{\mathrm{DGvector}}{,}{\mathrm{DiracSpinor}}{,}{\mathrm{EuclideanIndex}}{,}{\mathrm{ExtendedBra}}{,}{\mathrm{ExtendedKet}}{,}{\mathrm{ExtendedMatrix}}{,}{\mathrm{ExtendedTensor}}{,}{\mathrm{GenericMatrix}}{,}{\mathrm{HalfInteger}}{,}{\mathrm{IdentityMatrix}}{,}{\mathrm{InertSymbol}}{,}{\mathrm{NeedsMfence}}{,}{\mathrm{NumericalIndex}}{,}{\mathrm{PhysicsD3_2}}{,}{\mathrm{PhysicsD_2}}{,}{\mathrm{PhysicsFunction}}{,}{\mathrm{PhysicsTensor}}{,}{\mathrm{PhysicsVectors}}{,}{\mathrm{Physicsd_2}}{,}{\mathrm{Polynomial}}{,}{\mathrm{Procedure}}{,}{\mathrm{Procedure}}{,}{\mathrm{Physics}}{:-}{\mathrm{Projector}}{,}{\mathrm{ProjectorCore}}{,}{\mathrm{ProjectorInt}}{,}{\mathrm{ProjectorKind}}{,}{\mathrm{ProjectorSum}}{,}{\mathrm{QuantumNumber}}{,}{\mathrm{QuantumObject}}{,}{\mathrm{QuantumOperator}}{,}{\mathrm{SpaceIndexValue}}{,}{\mathrm{SpacetimeMetric}}{,}{\mathrm{SubSystem}}{,}{\mathrm{TensorIndex}}{,}{\mathrm{TensorStructure}}{,}{\mathrm{TensorVector}}{,}{\mathrm{UnitaryOperator}}{,}{\mathrm{VectorOperator}}{,}{\mathrm{Vectorial}}{,}{\mathrm{ac_symbol}}{,}{\mathrm{ac_symbol_local}}{,}{\mathrm{ac_var_local}}{,}{\mathrm{anticommutative}}{,}{\mathrm{c_symbol}}{,}{\mathrm{c_symbol_local}}{,}{\mathrm{c_var_local}}{,}{\mathrm{commutative}}{,}{\mathrm{gaugeindex}}{,}{\mathrm{nc_symbol}}{,}{\mathrm{nc_symbol_local}}{,}{\mathrm{nc_var_local}}{,}{\mathrm{noncommutative}}{,}{\mathrm{parameter}}{,}{\mathrm{spaceindex}}{,}{\mathrm{spacetimeindex}}{,}{\mathrm{spinorindex}}{,}{\mathrm{su2index}}{,}{\mathrm{su2matrixindex}}{,}{\mathrm{su3index}}{,}{\mathrm{su3matrixindex}}{,}{\mathrm{tetradindex}}{,}{\mathrm{AnnihilationOperator}}{,}{\mathrm{AppliableProcedure}}{,}{\mathrm{AppliedQuantumOperator}}{,}{\mathrm{BosonCreationOperator}}{,}{\mathrm{CommutativeMapping}}{,}{\mathrm{CreationOperator}}{,}{\mathrm{DGLiealgebraWithCoeff}}{,}{\mathrm{DGinhomogeneousform}}{,}{\mathrm{DGinhomogeneoustensor}}{,}{\mathrm{DGrepresentation}}{,}{\mathrm{DGtransformation}}{,}{\mathrm{DifferentialOperator}}{,}{\mathrm{ExpandableProduct}}{,}{\mathrm{ExtendedProjector}}{,}{\mathrm{ExtendedProjectorKind}}{,}{\mathrm{ExtendedQuantumOperator}}{,}{\mathrm{FermionCreationOperator}}{,}{\mathrm{FeynmanDiagramsTensors}}{,}{\mathrm{HermitianOperator}}{,}{\mathrm{LeviCivitaTensor}}{,}{\mathrm{LinearOperatorFunction}}{,}{\mathrm{LiteralSubscript}}{,}{\mathrm{NegativeExponent}}{,}{\mathrm{NonCommutativeProduct}}{,}{\mathrm{NotNestedIntegral}}{,}{\mathrm{PhysicsKnownFunction}}{,}{\mathrm{PrettyPrintedBra}}{,}{\mathrm{PrettyPrintedKet}}{,}{\mathrm{ProjectorKindDot}}{,}{\mathrm{ProjectorKindStar}}{,}{\mathrm{QuantumOperatorFunction}}{,}{\mathrm{SpaceTimeVectorApplied}}{,}{\mathrm{SpacetimeIndexValue}}{,}{\mathrm{TensorQuantumOperator}}{,}{\mathrm{TensorWithIndices}}{,}{\mathrm{VectorOperatorApplied}}{,}{\mathrm{AnnihilationCreationOperator}}{,}{\mathrm{BosonAnnihilationOperator}}{,}{\mathrm{DifferentialOperatorIndexed}}{,}{\mathrm{DifferentialOperatorSymbol}}{,}{\mathrm{EntirelyNotNestedIntegral}}{,}{\mathrm{ExtendedDifferentialOperator}}{,}{\mathrm{ExtendedMatrixRequiringEval}}{,}{\mathrm{FermionAnnihilationOperator}}{,}{\mathrm{FeynmanDiagramsTensorsIndexed}}{,}{\mathrm{PhysicsMatrixWithMatrixIndices}}{,}{\mathrm{TensorAllowWithoutIndices}}{,}{\mathrm{TensorInCurrentCoordinates}}{,}{\mathrm{TensorInternalRepresentation}}{,}{\mathrm{TensorWithAbstractIndices}}{,}{\mathrm{TensorWithNumericalIndices}}{,}{\mathrm{TypesettingStructureNeedsMfence}}{,}{\mathrm{VectorDifferentialOperator}}{,}{\mathrm{BosonAnnihilationCreationOperator}}{,}{\mathrm{CompositeDifferentiationOperator}}{,}{\mathrm{ExtendedDifferentialOperatorIndexed}}{,}{\mathrm{ExtendedDifferentialOperatorSymbol}}{,}{\mathrm{ExtendedVectorDifferentialOperator}}{,}{\mathrm{FermionAnnihilationCreationOperator}}{,}{\mathrm{VectorDifferentialOperatorCreated}}\right\}$ (8)
 > member( 'integer', (8) ); # neither is global
 ${\mathrm{false}}$ (9)

Using the Extension Mechanisms with Modules

 Often, when you are developing a package or other module (or even, occasionally, within a procedure definition), you may want to extend a facility such as type or convert with knowledge that is associated particularly to the code you are developing. For example, you may want to have your package implement several types and conversions. While both extension mechanisms can be used while taking advantage of modules or local data in procedures, only the modern extension mechanism can be used with extensions whose names are local to a module or procedure. Thus, for example, a module can define types whose names are among its exports, because type uses the modern extension mechanism, but combine can only be extended using global extension names, since it uses the classical extension mechanism exclusively.
 The advantage of defining a classical extension within a module (or procedure) is that the definition can make use of data local to the namespace in which it is defined.
 The most common example in which the modern extension mechanism is used in modules is extending type with local types. To do this, you must use TypeTools[AddType] to define the local type. This can be done in the body of a module definition if the local type is to be used only in the same session in which the module is defined. More often, however, you will want to be able to save your module (say, a package), and have the type definition be effective once the module is read from the repository in which it is saved.
 While you could manually check that your local types have been defined prior to use, and define them if they have not yet been defined, it is usually more convenient to use the load= module option. Any classical extensions defined in the procedure associated with the load= option must be declared global in that procedure (or the enclosing module). Note that it is the full name extendme/foo, rather than the extension name foo itself that must be declared global. The modern extension mechanism can also be used to define any local extensions. In this case, since there is no global name such as extendme/foo involved in the extension mechanism, only the extension name foo itself must be declared a local or exported local.

Example

 The following example package illustrates three distinct ways to use the extension mechanisms to associate extensions with the package facilities. (The package simply defines addition and multiplication for a peculiar representation of integers, along with several extensions to illustrate the techniques.)
 A new local type integer is defined and exported by the package. The local procedure setup is used to dynamically add this type whenever the package is read from a repository. The modern extension mechanism, that is supported by type, is used in this case.
 A print extension is defined in the module. The name print/INTEGER is declared global in the module definition, because print uses only the classical extension mechanism. Both the package and the global name print/INTEGER must be saved separately in a repository. (Saving the package does not automatically save print/INTEGER.)
 Finally, a custom conversion associated with this package is defined. While convert uses the classical extension mechanism, the setup procedure is used to add the conversion dynamically when the package is read.
 > FunnyIntegers := module()     description "strange integer operations";     option package, load = setup, unload = cleanup;     local setup, cleanup;     export a, m, integer;     global print/INTEGER;     # Define this global print' extension.     print/INTEGER := proc( str )         if StringTools:-IsDigit( str ) then             parse( str )         else             error "invalid funny integer"         end if     end proc;     # Use this procedure to extend type' and convert' dynamically     # when this package is read from a repository.     setup := proc()         global convert/INTEGER;         TypeTools:-AddType( 'integer', # define local integer type                s -> type( s, 'INTEGER( string )' )                         and StringTools:-IsDigit( op( 1, s ) ) );         convert/INTEGER := proc( n )              if type( n, ':-integer' ) then                  'INTEGER'( convert( n, 'string' ) )              elif type( n, 'integer' ) then                  n              else                  error "unable to convert"              end if          end proc;          NULL     end proc;     setup(); # make this available now     # Remove these dynamic definitions when the package is garbage collected.     cleanup := proc()         global convert/INTEGER;         TypeTools:-RemoveType( 'integer' );     convert/INTEGER := evaln( convert/INTEGER );     NULL     end proc;     # Define some exports     a := proc( s, t )         local s1, t1;         if type( [ s, t ], '[ integer, integer ]' ) then             s1 := parse( op( 1, s ) );             t1 := parse( op( 1, t ) );             convert( s1 + t1, 'INTEGER' )         else             error "invalid input"         end if     end proc;     m := proc( s, t )         local s1, t1;         if type( [ s, t ], '[ integer, integer ]' ) then             s1 := parse( op( 1, s ) );             t1 := parse( op( 1, t ) );         convert( s1 * t1, 'INTEGER' )         else             error "invalid input"         end if     end proc; end module:
 > type( 2, 'integer' );
 ${\mathrm{true}}$ (10)
 > type( 2, FunnyIntegers:-integer );
 ${\mathrm{false}}$ (11)
 > type( 'INTEGER'( "3" ), 'integer' );
 ${\mathrm{false}}$ (12)
 > type( 'INTEGER'( "3" ), 'FunnyIntegers:-integer' );
 ${\mathrm{true}}$ (13)
 > 'INTEGER'( "42" ); # printing works
 ${42}$ (14)
 > with( FunnyIntegers );
 $\left[{a}{,}{\mathrm{integer}}{,}{m}\right]$ (15)
 > type( 2, 'integer' ); # local type is now bound
 ${\mathrm{false}}$ (16)
 > type( 'INTEGER'( "3" ), 'integer' );
 ${\mathrm{true}}$ (17)
 > type( 'INTEGER'( "3" ), ':-integer' ); # global type is still accessible by using the :- prefix
 ${\mathrm{false}}$ (18)
 > m( 5, a( 2, 3 ) ); # error
 > m( 'INTEGER'( "5" ), a( 'INTEGER'( "2" ), 'INTEGER'( "3" ) ) );
 ${25}$ (19)
 > convert( 3, 'INTEGER' );
 ${3}$ (20)
 > lprint( convert( 3, 'INTEGER' ) );
 INTEGER("3")

How to Implement an Extensible Facility

 Your own Maple code can be made user-extensible by writing a module to manage the extensions. This is illustrated for the case of a single global command DoFoo, which does a simple transformation on expressions involving functions. The handling of unevaluated function calls that appear in input expressions is extensible by users by providing a function name and a transformation procedure to apply to all unevaluated function calls with the given function name.
 The extension names and associated implementations are stored in a table local to the module Foo. The module is not a package, in this case, so the exports can be accessed only by their fully qualified names (for example, Foo:-AddFoo). The module Foo itself exports three routines AddFoo, GetFoo and RemoveFoo. The AddFoo routine installs extensions in the local table fooTab managed by this module. There is also a RemoveFoo export for deleting extensions previously added. Finally, an existing extension can be queried by using the export GetFoo.
 > Foo := module()     export    AddFoo, GetFoo, RemoveFoo;     local    fooTab;     global    DoFoo;     fooTab := table():     AddFoo := proc( theName::symbol, implementation )         local    old_imp;         old_imp := NULL;         if assigned( fooTab[ theName ] ) then             WARNING( "replacing existing extension `%1'", theName );             old_imp := eval( fooTab[ theName ] )         end if;         fooTab[ theName ] := eval( implementation, 1 );         eval( old_imp, 1 )     end proc;     RemoveFoo := proc( theName::symbol )         if assigned( fooTab[ theName ] ) then             fooTab[ theName ] := evaln( fooTab[ theName ] )         end if;         NULL     end proc;     GetFoo := proc( theName::symbol )         if assigned( fooTab[ theName ] ) then             eval( fooTab[ theName ], 1 )         else             NULL         end if     end proc;     DoFoo := proc( expr, foo )         if assigned( fooTab[ foo ] ) then             subsindets( expr, 'specfunc'( 'anything', foo ), fooTab[ foo ] )         else             error "cannot do %1", foo         end if     end proc; end module:
 In the following examples, DoFoo is extended with three extensions. Note, in particular, that two extensions named G, one global and one local to the module M are defined, and are distinct.
 > F := 'F':
 > Foo:-AddFoo( 'F', proc( fn )     if type( fn, 'F( anything, anything )' ) then         'F'( op( 2, fn ), op( 1, fn ) )     else         fn     end if end proc ): Foo:-AddFoo( 'G', proc( fn )     if type( fn, 'G(algebraic)' ) then         'G'( - op( 1, fn ) )     else         fn     end if end proc ): M := module()     export G;     local setup;     load = setup;     setup := proc()         Foo:-AddFoo( 'G', proc( fn )             if type( fn, 'G(algebraic)' ) then                 'G'( 2 * op( 1, fn ) )             else                 fn             end if         end proc );         NULL     end proc;     setup(); end module: DoFoo( F( x, y ) + G( s ) + M:-G( s ) + H( s ), F );
 ${F}{}\left({y}{,}{x}\right){+}{G}{}\left({s}\right){+}{G}{}\left({s}\right){+}{H}{}\left({s}\right)$ (21)
 > DoFoo( F( x, y ) + G( s ) + M:-G( s ) + H( s ), G );
 ${F}{}\left({x}{,}{y}\right){+}{G}{}\left({-}{s}\right){+}{G}{}\left({s}\right){+}{H}{}\left({s}\right)$ (22)
 > DoFoo( F( x, y ) + G( s ) + M:-G( s ) + H( s ), M:-G );
 ${F}{}\left({x}{,}{y}\right){+}{G}{}\left({s}\right){+}{G}{}\left({2}{}{s}\right){+}{H}{}\left({s}\right)$ (23)
 > DoFoo( F( x, y ) + G( s ) + M:-G( s ) + H( s ), H ); # no extension for H