>> From: Joe Riel MUG'ers 13 June 1995 Inspired by problems encountered by several people attempting to install a Maple package, I wrote the following document to explain the basics of living with Maple. If you have any comments, suggestions, corrections, or additions to this document please send them to me and I'll try to incorporate them into an update. Thanks goes to Malcolm Brooks for patiently reviewing early versions and making many useful suggestions and corrections. Joe Riel jsr@sparc.sandiegoca.attgis.com or joseph.riel@sandiegoca.attgis.com Table of Contents ---------------- . INTRODUCTION 2. STARTUP & INITIALIZATION a. Startup Files b. Initialization Files c. Sequence of Events 3. FILES & LIBRARIES a. File Formats b. Libraries c. read d. readlib e. readlib-defined f. save g. March 4. PACKAGES 5. CHANGING MAPLE FILES a. Determining File Content b. Complete File Replacement c. Partial File Replacement d. Changing a Package 6. HELP FILES a. Maple Help Structure b. Helping Yourself c. Helping Others 7. SHARE LIBRARY APPENDIX: SOURCE CODE a. Anames b. Save c. ExtendPackage . INTRODUCTION =============== This document explains the use and operation of Maple startup files, initialization files, libraries, and packages, it also shows how to install your own patches or enhancements to Maple code. The information came from _First_Leaves:__A_Tutorial_Introduction_To_Maple_V_, the help files, and a fair amount of experimentation; I use Maple V Release 3 for Windows so some of the information may not apply to other versions or platforms and may be dated by release 4. 2. STARTUP & INITIALIZATION =========================== This section describes the files that effect how Maple is configured at startup and following a restart, and the sequence of events that occur. 2a. Startup Files ---------------- Startup files effect the look of a Maple worksheet. The Windows version of Maple V release 3 has a startup file named MAPLEV3.INI which is usually located in the C:\WINDOWS directory, the format is that of a Windows .INI file. Most of the settings can be changed via the menu selections in the Maple worksheet, for more information click on Interface Help in the Help menu, and then click on Windows Initialization (.ini) File. The setting of MapleLib in the [Options] section effects the startup value of libname, I comment this line out by putting a semicolon in front of it, see sections 2b and 2c for details about this variable. 2b. Maple Initialization Files ----------------------------- Maple initialization files assign values to Maple variables. Whenever Maple is started or restarted, Maple searches for two files, the system initialization file and the personal initialization file. The names and locations of these files are platform dependent, the following table gives the name and the default location of these two files for various platforms. Platform Type Name and Default Location ------- -------- --------------------------------------------- Unix system src/init in the Maple library directory personal .mapleinit in your home directory Macintosh system MapleInit in the Maple folder personal MapleInit in your System folder DOS/Windows system MAPLE.INI in the Maple LIB directory* personal MAPLE.INI in your current (working) directory DEC VMS system MAPLE.INI in the Maple LIB directory personal MAPLE.INI in your current (working) directory * The directory used for the system initialization file is actually the first item in the sequence assigned to the startup value of libname; the LIB directory is the default but that may be changed via the startup file and command line option -b, see section 1c for a description of how the startup value of libname is assigned. This fact is not described in the Maple manuals, I do not know whether it holds for other platforms. Both initialization files are sequences of Maple statements which are executed in order if the files are located. The system initialization file is executed first followed by the personal initialization file. Following is a partial listing of my system initialization file, with comments to explain what each section does. I do not use any personal initialization files. ############################################################################# # Sample System Initialization File # # _sys_init_file_read is a global variable that prevents this file from # being executed twice if Maple is started in the directory with the # system initialization file, in which case the personal and system # initialization files are the same. # if _sys_init_file_read = 1 then _sys_init_file_read := '_sys_init_file_read' else _sys_init_file_read := 1: # print(`reading system initialization file`); # Define global variables for each library, this simplifies saving or # reading files from specific libraries. maplelib := `d:/maplev3/lib`: # Main Maple library mylib := `d:/maplev3/mylib`: # Library of my own procedures updatelib := `d:/maplev3/update`: # Library for Maple patches fixlib := `d:/maplev3/fixlib`: # Library of my fixes to Maple # Set the global variable libname. The sequence determines the order that # the libraries are searched, fixlib is searched first, mylib last. libname := fixlib, updatelib, maplelib, mylib: # Define some handy procedures PP := proc(x:integer) if nargs <> 0 then interface(prettyprint = x) else interface(prettyprint) fi end: VB := proc(x:integer) if nargs <> 0 then interface(verboseproc = x) else interface(verboseproc) fi end: Not := proc(x) not x end: # May be composed with boolean procedures, # e.g. select(Not@type, ... ) Print := proc(x) local vb; vb := VB(); VB(2); if not type(x,procedure) then print(readlib(x)) else print(x) fi; VB(vb) end: # Define your own readlib-defined procedures syrup := 'readlib('syrup')': makepoly := 'readlib('makepoly')': # Install the patch for tutorial (see section 5c for details) unprotect('tutorial'): tutorial := 'op(1,map(readlib, ['tutorial', `tutorial/find`]))': # Protect the procedures protect('PP','Not','syrup','makepoly','tutorial'): # Update the status bar whenever 5000 words of 4 bytes have been read. words(5000): # Fill up ", "", and """ with zero so we have a known state. This is # needed for the Save Session menu item to work properly. 0:0:0: fi: ############################################################################ If you are unsure whether an initialization file is being read, add a print statement such as "print(`reading system initialization file`);". Nothing will be printed when Maple is started because printing is disabled until the initialization files are read, but subsequent restarts should cause the message to be printed if the file is being read. 2c. Sequence of Events --------------------- When Maple is started or restarted, the following sequence of events occurs, see ?maple and ?restart for details. 1) All packages are unloaded. 2) All variables are unassigned. 3) The startup value of libname is set as follows, if Maple was called with -b parameters, then they are used for libname, elif the startup file defined MapleLib, then it is used for libname, else the default (typically C:\MAPLEV3\LIB, for DOS/Windows) is used. 4) The file sysinit.m is read which assigns the many readlib-defined Maple procedures and packages. 5) The system initialization file is read. See section 2b for the location of this file, it depends on the startup value of libname. 6) The personal initialization file is read. See section 2b for the location of this file. During a restart the startup file is not reread, and any changes made to the look of a Maple worksheet, such as the font size and color, as well as changes to the interface settings, see ?interface, are not affected. 3. FILES & LIBRARIES ==================== 3a. File Formats --------------- There are two formats for Maple files, user format and internal Maple format. Files in user format are text files of Maple statements, the initialization files are in user format. Such files may be created with a text editor, they are executed by reading them into a Maple session with the read command [see below], the statements are executed as if directly entered in the session. Files in Maple internal format (.m files), which by convention have extension ".m", are not human readable, are created by the save command [see below], and consist of sequences of assignment statements. Reading such a file into a Maple session causes the statements to be assigned. A file in internal format is loaded much faster than the equivalent file in user format; however, because a file in internal format consists solely of assignment statements it is less flexible than a file in user format which may execute control statements and commands. 3b. Libraries ------------ A Maple library consists of two files, maple.lib and maple.ind, in a common directory; maple.lib is an archive of files in Maple internal format and maple.ind is an index to the archive. Archives provide a convenient means to create and distribute custom libraries, they are especially useful to DOS/Windows users of Maple since they remove the eight character, case insensitive filename restrictions of DOS. An archive is slightly larger than separate .m files since the index increases the space about 10%. 3c. read ------- The read command is used to read external files into a Maple session, see ?read for an explanation of its syntax. This section explains how the read command locates a specified file. Consider the following read statement, > read ``.mylib.`/fefifofum.bart`; The argument must evaluate to a valid Maple filename. The double backquotes at the start represents the null string, it is used because the catenation operator does not evaluate the first term. Assuming that mylib has been assigned the value `c:/maplev3/mylib` in one of the initialization files, this command is equivalent to, > read `c:/maplev3/mylib/fefifofum.bart`; Maple converts the Maple filename to the equivalent system filename, for a DOS system that is C:\MAPLEV3\MYLIB\FEFIFOFU.BAR The trailing M in FEFIFOFUM and T in BART were truncated because DOS permits file and directory names to be no more than eight characters and extensions no more than three characters. Maple then attempts to read a file with that pathname. If the Maple filename does not have an extension .m then Maple assumes that the file is in "user" format. If the Maple filename has an extension .m, then Maple assumes that the file is in Maple internal format, it also changes its search routine to include Maple archives. First, Maple searches for the equivalent system filename. If the file is found it is read and the search terminates. If the file is not found, Maple searches the archives in the directories specified by libname, reading the first file that matches the complete filename. The following example illustrates the subtleties of the read command. Suppose libname is set as, > libname := `c:/mainlib`, `c:/mainlib/sublib`, `c:/maplev3/lib`: then the command > read `c:/mainlib/sublib/test.m` causes Maple to search, in the following order, - the directory C:\MAINLIB\SUBLIB for the file TEST.M, - the archive at C:\MAINLIB for the file sublib/test.m, - the archive at C:\MAINLIB\SUBLIB for the file test.m. If a file is found, Maple reads it and terminates the search. If the sequence of libname were changed, > libname := `c:/mainlib/sublib`, `c:/mainlib`, `c:/maplev3/lib`: then the search order becomes, - the directory C:\MAINLIB\SUBLIB for the file TEST.M, - the archive at C:\MAINLIB\SUBLIB for the file test.m, - the archive at C:\MAINLIB for the file sublib/test.m. Since Maple searches for the converted system filename before searching Maple archives, it is possible that the read command will read a file whose name is different than that intended, e.g. both > read `c:/mainlib/test.m`: and > read `c:/mainlib/Test.m`: will read the system file C:\MAINLIB\TEST.M, if it exists, since DOS does not distinguish upper and lower case; for this reason it is wise not to store archives and external .m files in the same directory. 3d. readlib ---------- The procedure readlib reads a library file to define a specified name, see ?readlib. A call of the form > readlib(f) causes Maple to search for the file f.m in the directories specified by the libname sequence, the following pseudo-code shows the algorithm, for dir in [libname] do if read ``.dir.`/`.f.`.m` succeeds then exit If found, the file is read and the search terminated; the file must contain an assignment statement for the variable f, otherwise an error will occur; additional global variables may be assigned in the file. The help file ?readlib states that readlib may also be called in the form > readlib(f, file1, file2, ...) however, at least for the DOS/Windows version, only the first two arguments are used, the rest are ignored. This is unfortunate since otherwise this technique could be used to patch existing procedures, see section 5 for ways around this limitation. The readlib procedure uses a remember table so that a file read by one readlib call will not be reread by subsequent calls, this speeds execution but can be confusing when testing a library. Use the unload command to remove the procedure from the remember and unassign the name, see ?unload or details. 3e. readlib-defined ------------------ Maple makes extensive use of readlib-defined procedures to automatically load procedures and packages when needed. The following assignment is an example of a readlib-defined variable, > simplify := 'readlib('simplify')': When simplify is called for the first time, it is evaluated which causes the file simplify.m to be read from a Maple library which [presumably] assigns a procedure to simplify. You can use the eval procedure to determine whether a procedure is readlib-defined, > eval(simplify,1); readlib('simplify') Notice how simplify changes after it is first called, > simplify(a+b): > eval(simplify,1); proc() ... end It is customary to readlib-define a variable to itself, however, this isn't necessary, one can do the following, > simp := 'readlib('simplify')': Evaluating simp does not change its definition, though simplify changes. > simp(a+b): > eval(simp,1); readlib('simplify') 3f. save ------- The save command writes Maple variables to an external file, see ?save for details of the syntax. This section explains how save determines where to write a file, as with read the algorithm depends on whether the specified Maple filename has an extension ".m". Given the statement, > save name1, ..., nameN, file: the following pseudo-code summarizes Maple's actions, hostname := convert(file, hostfile) if the file does not have extension .m then write to hostname in user-format else for dir in [libname] do if head(file) = dir and dir has an archive then write to tail(file) in archive dir exit write to hostname in internal-format The head and tail functions indicate the relevant portions of the filename, they depend on the length of dir. As an example, if dir = `c:/maplev3` file = `c:/maplev3/mylib/test.m` then head(file) = `c:/maplev3` tail(file) = `mylib/test.m` 3f. March -------- March is a utility for creating and managing Maple archives, see ?march for details. 4. PACKAGES =========== Packages are collections of Maple procedures, usually tables in which the entries are procedures and the indices are "short forms" for the names of the procedures. The call with(pkg) assigns the procedures in the table pkg to the indices; if pkg is not an assigned name then pkg is read from a library with the command readlib(pkg), which should assign a table to pkg. This readlib call can be used to assign auxiliary functions which do not need a "short form" since they are not intended to be directly accessed by the user, these functions are customarily named "pkg/funcname". A procedure with the name "pkg/init" is considered to be an initialization procedure for the package pkg and is executed by a call to with(pkg). A function named init in the package table supercedes "pkg/init" as the initialization procedure. The following is the source code for a simple package, the subsequent Maple commands illustrate the effect of installing and using this package. ############################################################################# # Source code for a "sample" package, stored in file sample.src. # sample[hello] := proc() `sample/print`(`hello world`) end: sample[goodbye] := '`sample/print`(`goodbye, cruel world`)': `sample/print` := proc(s) print(`Simon says: `.s) end: `sample/init` := proc() `sample/print`(`Initializing sample`) end: save sample, `sample/print`, `sample/init`, ``.mylib.`/sample.m`: ############################################################################# > read sample.src: # install the sample package > restart: > with(sample); # define the procedures and variables in the package Simon says: Initializing sample [goodbye, hello] > hello(); Simon says: hello world > goodbye; # note that the entries in the table do not have to # be procedures Simon says: goodbye, cruel world By passing additional arguments to with, user-accessible procedures can be selectively defined, thus > restart: > with(sample, hello); Simon says: Initializing sample [hello] > goodbye; # goodbye should not be defined goodbye Individual functions in the packages supplied with Maple may be accessed with the package notation, > package[function](arguments) To permit this capability in your packages you should readlib-define them in one of your initialization files, thus ############################################################################# # Modification to an initialization file to permit accessing the functions # defined in the sample package without performing a with statement. # # sample := 'readlib('sample')': # the outer forward quotes delay the # # full assignment to sample until it is # # used; the inner forward quotes prevent # # a stack overflow due to a recursive # # definition of sample. ############################################################################# > restart: > sample[hello](); Simon says: hello world Because this method of calling a procedure does not invoke the with mechanism, the initialization procedure is not executed; the auxiliary functions are, however, assigned. The reason this works is that sample evaluates to a table via the readlib call, so the call sample[hello]() indexes that table. Large packages with many functions may use readlib-defined procedures, this reduces memory usage when only some of the functions are used. The following line is typical of what would go in the package definition file, sample[convert] := 'readlib(`sample/convert`)': The source file that defines and saves `sample/convert` might look like the following, `sample/convert` := proc() ... end: save `sample/convert`, ``.mylib.`/sample/convert.m`: 5. CHANGING MAPLE FILES ======================= If you are a frequent Maple user you have, alas, probably noticed a few bugs, or would like to change the operation of some procedures. The Maple designers had the foresight to make this not only possible but also rather simple. Since most Maple procedures are defined when needed by reading them from a library, a procedure can often be replaced by storing the updated version in a library which is searched before the original Maple library. 5a. Determining File Content --------------------------- A call to readlib assigns a value to its parameter but may also assign values to other global variables stored in the .m file. There is no Maple utility for inspecting the contents of an .m file [the m2src utility can be used to inspect release 2 .m files, see ?m2src], however, the procedure Anames, described in the appendix, is useful in this regard, the following usage returns the global variables assigned when tutorial, which is readlib-defined, is evaluated. > Anames('tutorial'); tutorial/doflagt, tutorial/doflagq, tutorial/doflaga, tutorial/doflagh, tutorial/doflagc1, tutorial/doflagc2, tutorial/doflageoq, tutorial/doflageof, tutorial/compare, tutorial/goodbye, tutorial/doflagx, tutorial/find, tutorial/mainmenu, tutorial/readline, tutorial/determine, tutorial/module, tutorial/prelude 5b. Complete File Replacement ---------------------------- The simplest method to update some of the variables assigned in a Maple file is to create a new file with all the variables, updated as necessary. The new file should be installed in a library which is searched before the main Maple library. Following is a partial listing of the source for replacing the `tutorial/find` procedure defined in the Maple file `tutorial.m`. [The `tutorial/find` procedure has a rather strange bug, pointed out to me by Malcolm Brooks, which causes an error if you have a library whose name ends with "xlib", such as "fixlib". The problem lies in an alphabet defined in the procedure which has two z's and no x's.] ############################################################################# # Source for installing updated tutorial function # readlib('tutorial'): # assign the original procedures `tutorial/find` := proc() ... end: # define the corrected procedure save tutorial, `tutorial/doflagt`, ..., `tutorial/prelude`, ``.fixlib.`/tutorial.m`: # save the original and corrected # procedures in the file tutorial.m # in the fixlib archive. ############################################################################# The fix is installed by reading the source into a Maple worksheet, > read ``.fixlib.`/src/tutorial.src` # read the source file 5c. Partial File Replacement --------------------------- While the previous method works, it wastes a bit of disk space since it duplicates reuseable code, i.e. the procedures contained in tutorial.m that weren't modified. You could, given write permission, choose to save the updated version in the standard Maple library rather than a new library, thus eliminating any wasted disk space, however, that is dangerous and I do not recommend it. A safer method to avoid duplication is to save only the updated procedures in a Maple library and then, in one of your initialization files, reassign the readlib-defined procedure name so that it reads the old and the new files when evaluated, the following shows how this can be done with the previous example, ############################################################################# # Modification to an initialization file which redefines tutorial so that it # reads the corrected `tutorial/find` procedure when tutorial is evaluated. # unprotect('tutorial'): tutorial := 'op(1,map(readlib, ['tutorial', `tutorial/find`]))': protect('tutorial'): A call to tutorial reads two files, `tutorial.m` in the Maple library and `tutorial/find.m` in the fixlib library, and assigns the procedures in both, the procedure assigned to tutorial is that returned from the call to readlib('tutorial'). This technique depends on the order in which Maple reads the two files, `tutorial/find.m` must be read last so that it replaces the procedure with the bug; this is assured by grouping the two filenames in a list rather than a set in the call to map and depending on the undocumented "fact" that map always applies its parametric function to a list in the same order, from left to right. 5d. Changing a Package --------------------- Packages are changed in the same way as procedures. If you want to change a readlib-defined procedure in a package then your task is simple, merely save the new procedure in a library that is searched before the main library. If the procedure is not readlib-defined or you want to add new functions to the package then you may either change and save the complete package definition or, to avoid wasting disk space, save only the new procedures and modify the initial assignment to the package name variable. The following examples illustrate both techniques. Changing a package by copying all the procedures is quite easy, especially if you use the Anames procedure to select the procedures that need saving. The following source code adds a procedure to the difforms package, the source for the Save procedure is given in the appendix. ############################################################################ # Source for adding the Hodge star operator to the difforms package. # oldnames := Anames('readlib('difforms', ``.maplelib.`/difforms.m`)'): unprotect('difforms'): difforms[hodge] := proc() ... end: Save(difforms, oldnames, ``.mylib.`/difforms.m`): ############################################################################ That method wastes a bit of space since the entire package together with the new procedures must be copied to a new library. Since most large packages use readlib-defined procedures the duplicated material is minimal, however, if disk space is at a premium then you might consider the following technique. The method is essentially that of section 5c for partially replacing a file, the new procedures are stored in a library and the name of the package is reassigned in an initialization file so that when evaluated the old and the new procedures are defined. First, save the new procedures as a package with the same name as the original package. The following source code shows how this is done. ############################################################################ # Source for creating a new difforms package with just the new procedures. # unprotect('difforms'): difforms := 'difforms': difforms[hodge] := proc() ... end: save difforms, ``.mylib.`/difforms.m`: ############################################################################ Next, reassign the package name in an initialization file so that when evaluated it combines the old and the new packages, this is best done through a procedure call. The following code shows the modifications to the initialization file, the source code for ExtendPackage is given in the appendix. ############################################################################ # Modification to an initialization file to extend the difforms package. # The functions are added when the package name is evaluated. # unprotect('difforms'): difforms := 'readlib('ExtendPackage')('difforms', maplelib)': protect('difforms'): ############################################################################ 6. HELP FILES ============= Maple comes with on-line help for its procedures and packages, if you write your own procedures or packages you will want to create help files to explain their operation. This section explains how to do this. a. Maple Help Structure ---------------------- A call to Maple help searches for a variable whose name is formed from the elements in the call, the following chart shows how the name is formed for the two types of help calls. > ?topic -----------> `help/text/`.topic > ?topic,subtopic -----------> `help/topic/text/`.subtopic If the variable is not assigned, Maple searches the libraries in the libname sequence. The variable must be assigned to a TEXT structure. b. Helping Yourself ------------------ The makehelp procedure, provided with release 3, simplifies installing a help file. Create the text file that you want to display and then convert it to a Maple TEXT structure and store in a library by using makehelp, > readlib(makehelp): > makehelp('sample', ``.mylib.`/help/src/sample.txt`, mylib): This will read the source ``.mylib.`/help/src/sample.txt` and save a file help/text/sample.m in the mylib library. See ?makehelp for details. c. Helping Others ---------------- To include help files in a package which is to be distributed as source code you need to save the help files as TEXT structures. This can be done with the save command, for example, > readlib(`help/text/sample`): > save `help/text/sample`, filename.txt; The file filename.txt can be appended to the distribution source code. 7. SHARE LIBRARY ================ The Maple share library contains a variety of useful procedures, packages, and worksheets contributed by Maple users. The procedures and packages are normally accessed via readshare, which is assigned by the command with(share), e.g. to access the Hurwitz procedure you would do the following, > with(share): > readshare(Hurwitz,calculus): If you frequently use a particular procedure in the share library you might consider readlib-defining it, for example, to readlib-define the Hurwitz procedure you could add the following statements to an initialization file, sharelib := `c:/maplev3/share`: # location of share library libname := libname, sharelib: Hurwitz := 'readlib('Hurwitz',``.sharelib.`/calculus/Hurwitz.m`)': APPENDIX: SOURCE CODE ====================== a. Anames -------- ############################################################################ # Anames returns a sequence of names assigned when the arguments to Anames # are evaluated, it is useful for determining the contents of Maple .m files. ############################################################################ Anames := proc() local oldnames: oldnames := anames(); map(eval, [args]); op({anames()} minus {oldnames}) end: # save Anames, ``.mylib.`/Anames.m`: b. Save ------ ############################################################################ # Save is similar to save except that it expands its arguments one level. # It is curious that an analagous procedure cannot be readily made for read. ############################################################################ Save := proc() subs('T' = args, proc() save T end)() end: # save Save, ``.mylib.`/Save.m`: c. ExtendPackage --------------- ############################################################################ # ExtendPackage adds the entries in the package table named pkg and located # in a user's library to the entries in the package table of the same name # located in the library oldlib. ############################################################################ ExtendPackage := proc(pkg,oldlib) unprotect(pkg); pkg := table(map(op@@2,[readlib(pkg,``.oldlib.`/`.pkg.`.m`),readlib(pkg)])); protect(pkg); eval(pkg) end: # save ExtendPackage, ``.mylib.`/ExtendPackage.m`: