Language and System Changes in Maple 6
|
Linking and Calling External Routines
|
|
|
This is probably one of the most significant new features of this release, because it allows unprecedented extensibility of the system.
|
|
Routines that are written in C (or in any language that exports a C interface) and are found in a library (shared library in UNIX, DLL in Windows) can now be linked into Maple dynamically, and then called as though they were native Maple routines. Additionally, external routines that take a function as an argument can be passed a Maple function. All data translations between Maple types (arbitrary precision integers and floats, procedures, Arrays, and so on) and external data types (such as int, double, function pointers, arrays, etc.) are handled automatically.
|
|
Two new Maple functions, define_external and call_external, are used to set up and use external functions. The external_calling help page describes these in detail, and gives a simple example. More detail can be found in the Programming Guide.
|
|
|
New and Enhanced Programming Features
|
|
|
Modules provide a means of encapsulating related data and the procedures to operate on that data. The module writer has full control over which data and procedures are externally visible (exported), and which are private to the module. Maple's new LinearAlgebra package has been implemented as a module.
|
|
Modules are created by executing a module definition, beginning with the new keyword module.
|
|
Two utility functions, exports and member, aid in manipulating modules. The exports function returns a sequence of the names of all the exported members of a module. The member function, which already existed to test for membership in a set, can determine if a given member is exported by a module.
|
|
Outside of a module, members are referred to by the syntax moduleName:- memberName. By first executing the command with(moduleName), all of the members of a module can be referred to by their memberName alone.
|
|
Since modules will almost invariably contain procedures, and since both modules and procedures can have implicitly declared local variables, the warning message that is displayed when a variable is implicitly declared local now indicates where this implicit declaration has taken place:
|
>
|
M := module() local a; export b;
a := proc() c := 2 end proc;
b := proc() d := 2 end proc;
e := proc() f := 2 end proc
end module;
|
Warning, `e` is implicitly declared local to module `M`
Warning, `c` is implicitly declared local to procedure M:-a
Warning, `d` is implicitly declared local to procedure M:-b
Warning, `f` is implicitly declared local to procedure M:-e
| |
| (1) |
|
As with local variables in procedures, local and exported variables in modules can be declared with type assertions (described later in this document). These assertions are active even outside of the scope of the module:
|
>
|
M := module() export a::integer; end module;
|
| (2) |
|
|
Examples
|
|
>
|
|
| (3) |
>
|
|
>
|
|
| (4) |
Within a module that overrides a given name, you can still refer to the global symbol of the same name by preceding it with the unary :- operator:
>
|
M := module() local a; export f; f := proc(x) a + :-a + x end; a := y+z end;
|
| (5) |
>
|
|
| (6) |
>
|
|
| (7) |
Operator Rebinding and the use Statement
To complement modules, Maple now has both global and scoped binding facilities. The use statement introduces a scope in which symbols and operators can be rebound (or overridden). These re-bindings are active within the statement sequence block of the use statement.
>
|

|
>
|
|
| (8) |
>
|
|
Warning, z5 is not a correctly formed package - option `package' is missing
| |
| (9) |
>
|
|
| (10) |
>
|
|
| (11) |
>
|
func := proc( m::`module`, a, b )
use `+` = m:-`+`, `*` = m:-`*` in
# within this block, `*` and `+` refer to the corresponding
# exports of the parameter m
a * b - a
end use
end proc;
|
| (12) |
>
|
|
| (13) |
To restore operators that have been rebound as a result of using the with command, you can issue a restart command
>
|
|
For more information, consult the help topics with, use, and module. Also refer to the Programming Guide, Chapter 6.
Exception Handling Mechanism
Inspired by Java's exception handling, Maple now has a new mechanism: the try-catch structure. Accompanying this is a new statement error which throws exceptions that can be caught by a try-catch. This new exception raising and handling architecture has been designed to remain backward compatible with the previous ERROR and traperror mechanism.
Maple's new error statement supports parameterized error messages, where the parameters can be described in a position-independent manner. Maple's WARNING function, which is used to issue warnings, supports parameters in the same way.
Maple automatically line-wraps error messages that are longer than one line, and automatically truncates any long error message parameters to about four lines or less. Truncated objects are indicated by "..." appearing at the end of them. This is to prevent enormous messages about large objects.
For backward compatibility, existing code that uses the ERROR, traperror, and lasterror will continue to work in this release.
Numeric Computation Events
Exceptional situations during numeric computations now trigger numeric events, which can be dealt with in a way compatible with IEEE hardware numerics specifications. Handlers can be installed to return a default value, or to trigger an actual exception. If desired, a handler can perform additional processing before carrying out one of these two actions. For more information, see numeric_events.
Directives
Maple now includes a minimal textual preprocessor, modelled after the C preprocessor.
The preprocessor directives are not part of the Maple language. They are part of the text-based interface ("preprocessor"), and are used during the processing of input files ("compiling").
The preprocessor does not work on files being read by the Maple read command, just as C's preprocessor does not work on files being read by standard I/O functions in a program. It is a compile-time facility, not a run-time facility.
To avoid potential collisions with Maple comments, Maple's preprocessor uses the dollar sign ("$") instead of the number sign ("#") as the preprocessor directive introducer. Furthermore, no white space may appear between the "$" and the directive's keyword.
For more information, see $define.
Inline Procedure Expansion
A procedure with no local variables and no lexically scoped variables, and whose body consists of a single expression or expression sequence (but not a statement), can be written with option inline, which causes the body of the procedure to be expanded inline wherever the procedure is called.
>
|
sqr := proc(x) option inline; x*x end proc;
|
| (14) |
>
|
quad := proc(x,y) sqrt( sqr(x+y) + sqr(x-y) ) end proc;
|
| (15) |
Features for Debugging and Profiling
You can now do statement-level profiling, for both time and storage use, of procedures and modules written in Maple. A new option traceproc has been added to the debugopts command. Giving it the name of a procedure turns on statement-level profiling for that procedures. (To turn it on for all procedures, use true in place of a procedure name.) After executing the procedure, a cumulative profile for each statement--as well as the entire procedure--can be displayed by calling the command showstat on the profiled procedure.
>
|
|
>
|
|
| (16) |
>
|
|
gcd := proc(aa, bb, cofa::name, cofb::name)
local Z, GCD, a, b;
|Calls Seconds Words|
PROC | 9 0.038 186317|
1 | 9 0.000 258| if 2 < nargs and member(cofa,`union`(indets(aa),indets(bb))) then
2 | 0 0.000 0| error "The optional 3rd argument given to `gcd` was %1. This argument to `gcd` is for returning the cofactor. It is not for specifying the variable in which to compute the GCD. If assigned, it could create a recursive definition of a name.", cofa
end if;
3 | 9 0.000 369| if nargs = 0 then
4 | 0 0.000 0| return 0
elif nargs = 1 then
5 | 0 0.000 0| if not type(aa,'polynom(numeric)') then
6 | 0 0.000 0| error "arguments must be polynomials over the rationals"
else
7 | 0 0.000 0| return sign(aa)*aa
end if
elif type(aa,'polynom(integer)') and type(bb,'polynom(integer)') then
8 | 8 0.000 0| Z := true
elif type(aa,'polynom(rational)') and type(bb,'polynom(rational)') then
9 | 0 0.000 0| Z := false
elif type(aa,'polynom(numeric)') and type(bb,'polynom(numeric)') then
10 | 0 0.000 0| return `gcd/float`(args)
elif select('type',indets([aa, bb],'specfunc(anything,RootOf)'),'algext') <> {} or select('type',indets([aa, bb],'anything^fraction'),'radext') <> {} or hastype([aa, bb],'nonreal') then
11 | 1 0.032 138295| return evala(('Gcd')(args))
else
12 | 0 0.000 0| GCD := frontend('`gcd/Freeze`',[normal(aa), normal(bb), nargs-2],'[{`*`, `+`}]');
13 | 0 0.000 0| if 2 < nargs then
14 | 0 0.000 0| cofa := GCD[2]
end if;
15 | 0 0.000 0| if 3 < nargs then
16 | 0 0.000 0| cofb := GCD[3]
end if;
17 | 0 0.000 0| return GCD[1]
end if;
18 | 8 0.000 3| if (type(aa,'`*`') or type(aa,'`^`')) and hastype(aa,'`+`') or (type(bb,'`*`') or type(bb,'`^`')) and hastype(bb,'`+`') then
19 | 0 0.000 0| a := normal(aa);
20 | 0 0.000 0| b := normal(bb);
21 | 0 0.000 0| if a = 0 and b = 0 then
22 | 0 0.000 0| if 2 < nargs then
23 | 0 0.000 0| cofa := 0
end if;
24 | 0 0.000 0| if 3 < nargs then
25 | 0 0.000 0| cofb := 0
end if;
26 | 0 0.000 0| GCD := 0
elif a = 0 then
27 | 0 0.000 0| GCD := `gcd/PartFactoredCase`(b,b,Z,args[3 .. nargs]);
28 | 0 0.000 0| if 2 < nargs then
29 | 0 0.000 0| cofa := 0
end if
elif b = 0 then
30 | 0 0.000 0| GCD := `gcd/PartFactoredCase`(a,a,Z,args[3 .. nargs]);
31 | 0 0.000 0| if 3 < nargs then
32 | 0 0.000 0| cofb := 0
end if
else
33 | 0 0.000 0| GCD := `gcd/PartFactoredCase`(a,b,Z,args[3 .. nargs])
end if
else
34 | 8 0.000 23| a := expand(aa);
35 | 8 0.000 16| b := expand(bb);
36 | 8 0.006 46643| GCD := `gcd/doit`(a,b,Z);
37 | 8 0.000 24| if 2 < nargs then
38 | 8 0.000 24| if a = 0 then
39 | 0 0.000 0| cofa := 0
elif type(GCD,'list') then
40 | 1 0.000 2| cofa := expand(GCD[2])
else
41 | 7 0.000 568| divide(aa,GCD,cofa)
end if
end if;
42 | 8 0.000 24| if 3 < nargs then
43 | 8 0.000 24| if b = 0 then
44 | 0 0.000 0| cofb := 0
elif type(GCD,'list') then
45 | 1 0.000 2| cofb := expand(GCD[3])
else
46 | 7 0.000 42| divide(bb,GCD,cofb)
end if
end if
end if;
47 | 8 0.000 0| `if`(type(GCD,'list'),GCD[1],GCD)
end proc
| |
For each statement in the procedure, the display shows the statement number, the number of times the statement was executed, the total number of seconds spent during the execution of the statement, and the total number of words of storage allocated during its execution. Additionally, a summary for the entire procedure is included in the first line.
The selectremove Function
The functions select and remove are often used together, to split a list or set into two disjoint sublists or subsets. A new function selectremove combines these two operations, performing them simultaneously in about the same amount of time that one of the individual operations would take.
Persistent Store
The persistent store lets Maple automatically load named objects from the library when they are first referred to. It makes the readlib function obsolete, and in fact, readlib has been changed to do nothing. The semantics of save and savelib have been changed to work with the persistent store.
The following example shows that the minimize function is not known to Maple at startup (the anames function returns an expression sequence of all assigned names of the given type), but is known as soon as it has been invoked.
>
|
|
>
|
|
>
|
|
| (17) |
>
|
|
The maple command savelib has been modified so that specifying the name of a '.m' file as the last argument is no longer necessary; if it is not specified, then each item is saved separately in the persistent store in such a way that it will automatically be loaded back in when referred to in a new session.
The Maple save command and savelib functions can no longer save the values of environment variables (for example, Digits) into .m files or a library.
The Maple library format previously limited the length of members of the library to 51 characters. This limit has been increased to 115 characters.
The Maple library archive manager, march, which is used to maintain libraries, is no longer a separate, operating system command-line utility. Instead, it is now a built-in Maple function. Some of the operations that march can perform, such as library reindexing and packing, are now performed automatically as required. (These operations can still be invoked manually by using march as required).
|
|
Changes to the Type System
|
|
|
Indexed types have been extended to work similarly to 'function' types. Basically, type(a, b[c]) and type(a, b[c](args)) now mean `type/b`[c](a) and `type/b`[c](a, args) if and only if `type/b` exists.
|
|
Otherwise, type(a, b[c]) still means
|
|
type(a, indexed) and {op(0, a) = b} and type([op(a)], [c])
|
|
and type(a, b[c](args)) still means
|
|
type(a, function) and op(0, a) = b[c] and type([op(a)], [args])
|
|
The types And, Or, Not, nonnegint, posint, nonposint, negint, negative, and positive are now all built-in. The types nonnegative and nonpositive have also been included as built-in types.
|
|
Type Assertions
|
|
|
Local variables can be declared with type assertions. The syntax is the same as that used for parameters. Whenever an assignment is made to a local variables with such a type assertion, the type of the right-hand side is checked after evaluation, but before the assignment is done. If the type of the right-hand side does not match, an assertion failure exception is raised.
|
|
Similarly, the left-hand side of any assignment can contain a type assertion that is checked before the assignment is carried out.
|
|
The setting of kernelopts(assertlevel) controls whether these type assertions are checked. A setting of 0 turns off all assertion checking. A setting of 1 checks only assertions made using the ASSERT function. A setting of 2 checks ASSERT assertions, and assignment-type assertions.
|
>
|
kernelopts(assertlevel=2);
|
| (18) |
>
|
F := proc(x) local a::integer; a := x end;
|
| (19) |
|
The definition of the type atomic has been rationalized so that now it returns true for any object x where map( f, x ) = f(x):
|
>
|
type( 3 + 4*I, atomic );
|
| (20) |
| (21) |
>
|
type( 3 + a*I, atomic );
|
| (22) |
| (23) |
|
This includes the type symbol, complex(extended_numeric), indexed, string, procedure, and module.
|
|
Type suffixed is used to test if a symbol or string is a given symbol or string suffixed by an object of a given type.
|
>
|
type( _Z3, 'suffixed(_Z, integer)' );
|
| (24) |
>
|
soln := dsolve( {(D@@2)(f)(x) = -f(x)}, f(x) );
|
| (25) |
>
|
indets( soln, 'suffixed(_C, integer)' );
|
| (26) |
|
If you do not include the type, then it will just check if the symbol or string is prefixed by the given symbol or string.
|
|
Type procedure has been enhanced to support the detection of signatures for procedures whose argument types are declared.
|
>
|
p := proc( x::integer, s::string ) print( s ); x+2 end:
|
>
|
type( p, 'procedure( integer, string )' );
|
| (27) |
>
|
type( p, 'procedure( integer, float )' );
|
| (28) |
|
Type module is used to test if an expression is a module, as created by a module definition. Type module can take optional arguments, which must be symbols or expressions of the form symbol::type. If these optional arguments are specified, the module being checked must export members with names that correspond to those symbols, for the type check to return true:
|
>
|
M := module() export A; A := proc(x::integer) 2-x end end;
|
| (29) |
| (30) |
| (31) |
| (32) |
>
|
type(M,'`module`(A::procedure(integer))');
|
| (33) |
|
|
|
Importing and Exporting
|
|
|
Binary Files: readbytes, writebytes
|
|
The readbytes and writebytes functions now accept Matrix, Vector, and Array structures with any hardware datatype. The data in such a structure is read or written as a sequence of bytes, without regard to their meaning within the structure:
|
>
|
h := Array([[1,2/3],[3,evalf(Pi)],[5,6],[7,8]],
order=C_order, datatype=float[8]);
|
| (34) |
>
|
writebytes("/tmp/data.tmp",h);
|
>
|
close("/tmp/data.tmp");
|
>
|
g := Array(1..8, 1..8, order=C_order, datatype=integer[1]):
|
>
|
readbytes("/tmp/data.tmp",g);
|
| (35) |
|
The procedure curry returns a procedure which is derived from the first argument by currying on the remaining arguments, if any, in procedure application.
|
|
The procedure rcurry (right curry) is similar to curry, but curries on the specified arguments from the right of the parameter list.
|
|
Currying a procedure produces a new procedure that has some of the arguments of the original procedure specialized. For example, currying a two-argument procedure f on its first argument produces the procedure g of a single argument with the property that .
|
>
|
BesselJ0 := curry( BesselJ, 0 );
|
| (36) |
| (37) |
| (38) |
| (39) |
| (40) |
| (41) |
|
The left-fold operator foldl composes a binary "operator" f, with "identity" or initial value id onto its arguments r1, ..., rn (which may be zero in number), associating from the left. For example, given three arguments a, b, and c and initial value id, we have
|
|
foldl( f, id, a, b, c) = f( f( f( id, a ), b ), c )
|
|
|
Miscellaneous
|
|
|
On 32-bit computers, objects can now have 2^26-1 items in them instead of 2^17-1. This means, for example, that polynomials can now have around 33 million terms instead of 60 thousand.
|
|
Small integers (with magnitude less than 2^30 on 32-bit machines, or 2^62 on 64-bit machines) are now stored more efficiently. Instead of being stored as a pointer to a structure that describes the integer, they are stored in the pointer itself. This is an internal change, completely invisible to the user, except that it results in improved arithmetic performance.
|
|
Functions Moved to the Kernel
|
|
The following often-used functions have been moved to the kernel: lhs, rhs, and remove.
|
|
Name Creation through Concatenation
|
|
The symbol for concatenating two objects has been changed from a dot (".") to a pair of vertical bars ("||"). The dot character is now used for Matrix and Vector multiplication.
|
| (42) |
|
Previously, break and next were Maple names with special definitions. These have now been made into language keywords. Other new keywords are catch, error, exports, finally, module, return, try, and use. This brings the total number of keywords to 42.
|
|
The ending delimiter for the if statement was previously fi, while the ending delimiter for loops was od. The ending delimiter for procedures was simply end. With the addition of several new structures (try, module, and use), this scheme of reverse-spelling the beginning delimiter was unworkable. Therefore, a consistent ending delimiter scheme has been adopted.
|
|
The keyword end can be used as the ending delimiter for any structure. The end can optionally be followed by the beginning delimiter keyword, and if supplied, must be the correct keyword. So, an if statement can end with either end or end if. A loop (for..while..do) can end with end or end do, and so on.
|
|
For backwards compatibility, fi and od are still accepted.
|
|
In pretty-printed output, Maple uses either the shortest possible terminator, or end followed by the beginning delimiter keyword, depending on the setting of interface(longdelim):
|
>
|
interface(longdelim=false);
|
| (43) |
>
|
proc(x) if x < 0 then -x else x end if end;
|
| (44) |
>
|
interface(longdelim=true);
|
| (45) |
>
|
proc(x) if x < 0 then -x else x fi end;
|
| (46) |
|
In line-printed output, Maple always uses the two-word form of the ending delimiter.
|
|
Using subsop on Procedures and Modules
|
|
When the subsop function is applied to procedures or module definitions, certain restrictions are now enforced. When substituting the parameter, local, or export sequences, the length of the new sequence must match that of the old. One cannot add or remove a parameter, local, or export using subsop.
|
|
When you write a Maple program in a text file (not a worksheet), you can use the preprocessor directive to include the new include file assert.mi, distributed with the Maple system. It includes preprocessor definitions for Assert and Assert2 that expand to one and two argument calls to the command ASSERT, respectively, when the preprocessor macro _DEBUG_ is defined, and expand to nothing when it is not.
|
|
The anames function can be called with the argument environment, and will return a list of all defined environment variables.
|
|
Note that user-defined environment variables (those beginning with "_Env") are not saved in the environment unless they are modified within a procedure, and so they will only appear in the result of anames(environment) if called from within a procedure in which they have been modified (or in a procedure called from such a procedure):
|
| (47) |

| (48) |
>
|
f := proc() _EnvFoo := 4; anames(environment) end proc;
|
| (49) |

| (50) |
| (51) |
|
Consecutive Maple string constants, separated from one another only by whitespace, are merged into one long string constant at parse time. Note that this is not a concatenation operation, but merely a notational convenience for writing long strings.
|
| (52) |
>
|
printf("This is a long message that I did"
" not want to write on one line.\n");
|
This is a long message that I did not want to write on one line.
| |
|
Functions that have special evaluation rules can sometimes behave in unexpected ways; details of what these "special" rules are explained in great detail in the help page spec_eval_rules.
|
|
Constructors and Other Special Names
|
|
Maple has several functions that are not really functions at all. Instead they are special names that are recognized by the simplifier to perform special operations.
|
|
Four of these, Float, Integer, Fraction, and Complex, are constructors. A constructor builds a data object of the specified type. Because these functions are recognized by the simplifier, they cannot be redefined, and you cannot trace them or set a breakpoint in them.
|
|
Another function recognized by the simplifier is factorial, which is computed at simplification time. As with the constructors, it cannot be redefined either.
|
|
Two other special names, restart and tracelast, are recognized by the top-level evaluator before any evaluation takes place. These two names are recognized only at the top level, and can safely be used as local variable names.
|
|
The dismantle function used to display nearly empty Maple tables with hundreds of lines of "[NIL]" entries. This has been shortened to a single "." for each "[NIL]", and consecutive dots are all printed on the same line.
|
>
|
dismantle(table([a=x, b=y, c=z]));
|
TABLE(4)
EXPSEQ(1)
NAME(4): false #[protected]
HASHTAB(257)
...............................................................
HASH(7)
NAME(4): a
NAME(4): x
....
..................................................................................................................................................................
HASH(7)
NAME(4): c
NAME(4): z
....
...........
HASH(7)
NAME(4): b
NAME(4): y
....
.................
| |
|
New Data Structure for modp1
|
|
The modp1 structure now keeps track of the indeterminate and modulus of the given modp1 polynomial. Previously only the coefficients of the modp1 polynomials were stored. This was done in a LIST or INTPOS data structure depending on the size of the modulus.
|
|
Because the new ZPPOLY data structure stores the modulus and indeterminate, both these pieces need to be passed to any routine that creates a ZPPOLY from scratch, or from data that does not contain this information. These routines are: ConvertIn, Build, Zero, One, Constant, Randpoly, and Interp. In all these cases the indeterminate must be passed as an extra argument to the modp1 function call.
|
>
|
modp1(Randpoly(2,x),11);
|
| (53) |
|
A number of formats and modifiers have been added to the printf and scanf family of functions.
|
•
|
The "Z" and "z" modifiers are used to print or scan complex numeric values in one of two formats. The "Z" modifier prints complex values in the form x+yi or x-yi, where x and y are the real and imaginary parts, and i is the current setting of interface(imaginaryunit)
|
|
The "z" modifier, which must be followed by a character, prints the real and imaginary parts separated by the specified character.
|
•
|
The "{}" modifier is used to specify formatting or scanning options for rectangular tables (Array, Matrix, and Vector objects).
|
•
|
The "%Q" and "%q" formats are similar to "%A" and "%a" respectively, except that "%Q" and "%q" will consume all remaining arguments and print them as an expression sequence.
|
•
|
The "%Y" and "%y" formats will print or scan a floating point value in byte-order independent double-precision IEEE hex-dump format, using uppercase A to F for the digits 10 to 15 if "%Y" was used, or lowercase a to f if "%y" was used.
|
|
In previous versions of Maple, the digraphs "(*", "*)", "(|", and "|)" could be used in place of "{", "}", "[", and "]" respectively. This feature, which was originally provided for use with EBCDIC terminals connected to old IBM mainframe computers, has been removed for this release.
|
|
|