Data Type Coercion
In Maple, procedures can be declared with strict types on their parameters. Often there are multiple data types that are similar enough that a procedure should be able to deal with any of them. However having to code the procedure to be able to handle each different data type can be complex. To help with writing such procedures, Maple has data type coercion. That is, the ability to convert various data types into one expected type.
The most common way to declare that a parameter can be coercible is to put a tilde (~) in front of a standard Maple type, for example m::~Matrix instead of m::Matrix. If the command ~Matrix exists as either an export of the current module, or a top-level command, then that command will be invoked when the input does not match the type Matrix.
p := proc( m::~Matrix ) whattype( m ); end;
p:=procm::`~Matrix`whattype⁡mend proc
The above procedure is able to work on various different data types while only seeing the expected type within the function.
p( Array( [ [ 1, 2 ], [ 3, 4 ] ] ) );
Matrix
p( Vector[row]( [ 5,6,7 ] ) );
p( [ [ 1,2,3 ], [ 4,5,6 ], [7,8,9] ] );
If the given type cannot be converted to the expected type, then an exception is raised.
p( 1.4 );
Error, invalid input: p expects its 1st argument, m, to be coercible to the type Matrix, but received 1.4
Currently Maple has implementations of ~Matrix, ~Vector, and ~Array. To see what data types are accepted by these functions, see the ~Array help page. It is anticipated that more top-level coercion commands will be implemented in future versions of Maple. For standard Maple types, it is advised that you either use the long form described below, or use a module export to define your own coercions. The following example shows how to declare a private coercion routine inside a module. The use of ModuleApply makes the module work the same way as a procedure, but because it is a module, other methods and data can be associated with it.
Frac := module() local ModuleApply, ~rational; ~rational := proc( a::float ) convert(a,rational); end; ModuleApply := proc( r::~rational ) frac(r); end; end module;
Frac:=modulelocalModuleApply,`~rational`;end module
Frac(1.5);
12
Frac(3/2);
Coercion is able to make changes not just to the container, but it will also adjust the data type of rtables to match the expected data types, when possible.
p_type := proc( m::~Matrix( datatype=float ) ) rtable_options(m,datatype); end;
p_type:=procm::`~Matrix`⁡datatype=floatrtable_options⁡m,datatypeend proc
p_type( Matrix( [[1,2],[3,4]], datatype=integer ) );
float8
p_type( Matrix( [[1/2,2/3],[3/4,4/5]], datatype=rational ) );
If the given expression cannot be converted to the expected type, then an exception is raised.
p_type( Matrix( [[1*I,2*I],[3*I,4*I]], datatype=complex ) );
Error, invalid input: p_type expects its 1st argument, m, to be coercible to the type Matrix(datatype = float), but received Matrix(2, 2, [[I,2*I],[3*I,4*I]], datatype = complex)
Many of the data type coercions from one form of an rtable to another can be performed without actually creating a copy of the data. Maple can create an alias to the same data set that makes it appear to fit the desired type. However for some of the conversions, lists to rtables or changing rtable types a copy of the data is necessary. When a copy is created, modifying the rtable within the procedure will not effect the rtable that was actually passed. Therefore procedures that intend to work in-place should not enable coercion.
A value of NULL returned from the ~ procedure doing the coercion indicates the coercion failed. This will result in the standard "invalid input" error from the outer procedure.
~Array(4);
Calling ~Array directly with an integer as input results in NULL because this is not a valid coercion. Likewise, passing 4 to a procedure that declares its first parameter as ~Array will invoke ~Array(4) behind the scenes, get NULL, and decide that 4 could not be coerced so an error should be raised.
proc( a::~Array ) a; end(4);
Error, invalid input: unknown expects its 1st argument, a, to be coercible to the type Array, but received 4
coerce() Long Form
Maple also allows programmers to explicitly specify how they want coercions to occur. This is done by using the coerce() parameter modifier. The coerce modifier allows a programmer to specify a sequence of types and coercion procedures. A coercion procedure is a procedure that accepts a single typed parameter and converts that parameter into a new expression. When the main procedure is called the argument is type checked against the coercion procedure's parameter types. The first coercion procedure whose parameter's type matches the type of the argument is called. This is similar to how overloaded procedures work. The return value of the matching coercion procedure is then used as the parameter's value.
p_string := proc( s::coerce( string, (s::name)->convert(s,string) ) ) s; end;
p_string:=procs::coerce⁡string,s::name→convert⁡s,stringsend proc
If the parameter passed is of type string, then it matches the type listed in the coerce statement and thus passes this string through unchanged.
p_string( "a string" );
a string
If the parameter passed is of type name, the second item, (s::name)->convert(s,string) converts the name to a string and that string is passed as p_string's argument.
p_string( `a name` );
a name
If there is no matching coercion procedure, then an error is raised
p_string( 1123 );
Error, invalid input: p_string expects its 1st argument, s, to be of type string or coercible via (s::name) -> convert(s,string), but received 1123
The previous example uses the arrow form of a procedure declaration, however a named procedure can also be used.
p_proc := proc( A::coerce( Matrix, LinearAlgebra:-Simplify ) ) A; end proc:
p_proc( x + <1,2;3,4> );
1+x234+x
Note that LinearAlgebra:-Simplify doesn't always return a Matrix, so p_proc must expect to handle these other types. Alternately you could wrap the call in another procedure that checks its return type and returns NULL if it wasn't the type you wanted.
LASimplify := proc(a) local r := LinearAlgebra:-Simplify(a); if r::Matrix then return r; else return NULL; end if; end proc;
LASimplify:=procalocalr;r:=LinearAlgebra:-Simplify⁡a;ifr::MatrixthenreturnrelsereturnNULLend ifend proc
p_proc := proc( A::coerce( Matrix, LASimplify ) ) A; end proc:
p_proc( a_symbol );
Error, invalid input: p_proc expects its 1st argument, A, to be of type Matrix or coercible via LASimplify, but received a_symbol
Multiple types and coercion routines can be specified. They are tested in order.
ModNearestP := proc( a::integer, p::coerce( prime, (a::posint)->nextprime(a), (a::negint)->`if`(type(-a,prime),-a,nextprime(-a)) ) ) a mod p; end proc;
ModNearestP:=proca::integer,p::coerce⁡prime,a::posint→nextprime⁡a,a::negint→if⁡type⁡−a,prime,−a,nextprime⁡−aamodpend proc
ModNearestP(5,3);
2
ModNearestP(5,4);
0
ModNearestP(5,-3);
One powerful aspect of the ~Array coercion method is that it allows the procedure author to work using either 1-based or 0-based arrays. Using the coercion declaration A::~Array(0..) will ensure the procedure always gets an array with index A[0] as its first element. This ~ shortcut can be spelled out in long form as follows: A::coerce( {Array(0..)}, ~Array(0..) ). Here, if the passed input does not match the type {Array(0..)}, then ~Array(input,0..) will be invoked to do the coercion. There is a notable special case in this declaration; we cannot declare this as A::coerce( Array(0..), ~Array(0..) ) because Array(0..) is ambiguous as a coercion function or as a type. As a rule, all functional forms are treated as coercion functions. Putting curly brackets around {Array(0..)} forces it to be recognized as a type.
See Also
Array
list
listlist
Procedure Parameters
Procedures
rtable
Vector
~Array
Download Help Document