Lisp and Maxima
===============

Maxima is written in Lisp, and it is easy to access Lisp functions and variables from Maxima and vice versa.  Lisp and Maxima symbols are distinguished by a naming convention.  A Lisp symbol which begins with a dollar sign $ corresponds to a Maxima symbol without the dollar sign.

A Maxima symbol which begins with a question mark ? corresponds to a Lisp symbol without the question mark.  For example, the Maxima symbol foo corresponds to the Lisp symbol $FOO, while the Maxima symbol ?foo corresponds to the Lisp symbol FOO.  Note that ?foo is written without a space between ? and foo; otherwise it might be mistaken for describe ("foo").

Hyphen -, asterisk *, or other special characters in Lisp symbols must be escaped by backslash \ where they appear in Maxima code.  For example, the Lisp identifier *foo-bar* is written ?\*foo\-bar\* in Maxima.

Lisp code may be executed from within a Maxima session.  A single line of Lisp (containing one or more forms) may be executed by the special command :lisp.  For example,

     (%i1) :lisp (foo $x $y)

calls the Lisp function foo with Maxima variables x and y as arguments.  The :lisp construct can appear at the interactive prompt or in a file processed by batch or demo but not in a file processed by load batchload translate_file or compile_file

The function to_lisp opens an interactive Lisp session. Entering (to-maxima) closes the Lisp session and returns to Maxima.

Lisp functions and variables which are to be visible in Maxima as functions and variables with ordinary names (no special punctuation) must have Lisp names beginning with the dollar sign $.

Maxima is case-sensitive, distinguishing between lowercase and uppercase letters in identifiers.  There are some rules governing the translation of names between Lisp and Maxima.

 1. A Lisp identifier not enclosed in vertical bars corresponds to a Maxima identifier in lowercase.  Whether the Lisp identifier is uppercase, lowercase, or mixed case, is ignored.  E.g., Lisp $foo, $FOO, and $Foo all correspond to Maxima foo.  But this is because $foo, $FOO and $Foo are converted by the Lisp reader by default to the Lisp symbol $FOO.

 2. A Lisp identifier which is all uppercase or all lowercase and enclosed in vertical bars corresponds to a Maxima identifier with case reversed.  That is, uppercase is changed to lowercase and lowercase to uppercase.  E.g., Lisp |$FOO| and |$foo| correspond to Maxima foo and FOO, respectively.

 3. A Lisp identifier which is mixed uppercase and lowercase and enclosed in vertical bars corresponds to a Maxima identifier with the same case.  E.g., Lisp |$Foo| corresponds to Maxima Foo.

The #$ Lisp macro allows the use of Maxima expressions in Lisp code. #$EXPR$ expands to a Lisp expression equivalent to the Maxima expression EXPR.

     (msetq $foo #$[x, y]$)

This has the same effect as entering

     (%i1) foo: [x, y];

The Lisp function displa prints an expression in Maxima format.

     (%i1) :lisp #$[x, y, z]$
     ((MLIST SIMP) $X $Y $Z)
     (%i1) :lisp (displa '((MLIST SIMP) $X $Y $Z))
     [x, y, z]
     NIL

Functions defined in Maxima are not ordinary Lisp functions.  The Lisp function mfuncall calls a Maxima function.  For example:

     (%i1) foo(x,y) := x*y$
     (%i2) :lisp (mfuncall '$foo 'a 'b)
     ((MTIMES SIMP) A B)

Some Lisp functions are shadowed in the Maxima package, namely the following.

   complement     continue      //
   float          functionp     array
   exp            listen        signum
   atan           asin          acos
   asinh          acosh         atanh
   tanh           cosh          sinh
   tan            break         gcd

Programming

Garbage Collection
==================

Symbolic computation tends to create a good deal of garbage (temporary or intermediate results that are eventually not used), and effective handling of this can be crucial to successful completion of some programs.

Under GCL, on UNIX systems where the mprotect system call is available (including SUN OS 4.0 and some variants of BSD) a stratified garbage collection is available.  This limits the collection to pages which have been recently written to.  See the GCL documentation under ALLOCATE and GBC.  At the Lisp level doing (setq si::*notify-gbc* t) will help you determine which areas might need more space.

For other Lisps that run Maxima, we refer the reader to the documentation for that Lisp on how to control GC.

Introduction to Program Flow
============================

Maxima provides a do loop for iteration, as well as more primitive constructs such as go.

Functions and Variables for Program Flow
========================================

 -- Function: backtrace ()
 -- Function: backtrace (N)

     Prints the call stack, that is, the list of functions which called the currently active function.

     backtrace() prints the entire call stack.

     backtrace (N) prints the N most recent functions, including the currently active function.

     backtrace can be called from a script, a function, or the interactive prompt (not only in a debugging context).

     Examples:

       * backtrace() prints the entire call stack.

               (%i1) h(x) := g(x/7)$
               (%i2) g(x) := f(x-11)$
               (%i3) f(x) := e(x^2)$
               (%i4) e(x) := (backtrace(), 2*x + 13)$
               (%i5) h(10);
               #0: e(x=4489/49)
               #1: f(x=-67/7)
               #2: g(x=10/7)
               #3: h(x=10)
                                             9615
               (%o5)                         ----
                                              49

       * backtrace (N) prints the N most recent functions, including the currently active function.

               (%i1) h(x) := (backtrace(1), g(x/7))$
               (%i2) g(x) := (backtrace(1), f(x-11))$
               (%i3) f(x) := (backtrace(1), e(x^2))$
               (%i4) e(x) := (backtrace(1), 2*x + 13)$
               (%i5) h(10);
               #0: h(x=10)
               #0: g(x=10/7)
               #0: f(x=-67/7)
               #0: e(x=4489/49)
                                             9615
               (%o5)                         ----
                                              49

     Debugging

 -- Special operator: do
 -- Special operator: in

     The do statement is used for performing iteration.  Due to its great generality the do statement will be described in two parts. First the usual form will be given which is analogous to that used in several other programming languages (Fortran, Algol, PL/I, etc.); then the other features will be mentioned.

     There are three variants of this form that differ only in their terminating conditions.  They are:

       * for VARIABLE: INITIAL_VALUE step INCREMENT thru LIMIT do BODY

       * for VARIABLE: INITIAL_VALUE step INCREMENT while CONDITION do BODY

       * for VARIABLE: INITIAL_VALUE step INCREMENT unless CONDITION do BODY

     (Alternatively, the step may be given after the termination condition or limit.)

     INITIAL_VALUE, INCREMENT, LIMIT, and BODY can be any expressions.  If the increment is 1 then "step 1" may be omitted.

     The execution of the do statement proceeds by first assigning the INITIAL_VALUE to the VARIABLE (henceforth called the control-variable).  Then: (1) If the control-variable has exceeded the limit of a thru specification, or if the condition of the unless is true, or if the condition of the while is false then the do terminates.  (2) The BODY is evaluated.  (3) The increment is added to the control-variable.  The process from (1) to (3) is performed repeatedly until the termination condition is satisfied.  One may also give several termination conditions in which case the do terminates when any of them is satisfied.

     In general the thru test is satisfied when the control-variable is greater than the LIMIT if the INCREMENT was non-negative, or when the control-variable is less than the LIMIT if the INCREMENT was negative.  The INCREMENT and LIMIT may be non-numeric expressions as long as this inequality can be determined.  However, unless the INCREMENT is syntactically negative (e.g. is a negative number) at the time the do statement is input, Maxima assumes it will be positive when the do is executed.  If it is not positive, then the do may not terminate properly.

     Note that the LIMIT, INCREMENT, and termination condition are evaluated each time through the loop.  Thus if any of these involve much computation, and yield a result that does not change during all the executions of the BODY, then it is more efficient to set a variable to their value prior to the do and use this variable in the do form.

     The value normally returned by a do statement is the atom done.  However, the function return may be used inside the BODY to exit the do prematurely and give it any desired value.  Note however that a return within a do that occurs in a block will exit only the do and not the block.  Note also that the go function may not be used to exit from a do into a surrounding block.

     The control-variable is always local to the do and thus any variable may be used without affecting the value of a variable with the same name outside of the do.  The control-variable is unbound after the do terminates.

          (%i1) for a:-3 thru 26 step 7 do display(a)$
                                       a = - 3

                                        a = 4

                                       a = 11

                                       a = 18

                                       a = 25

          (%i1) s: 0$
          (%i2) for i: 1 while i <= 10 do s: s+i;
          (%o2)                         done
          (%i3) s;
          (%o3)                          55

     Note that the condition while i <= 10 is equivalent to unless i > 10 and also thru 10.

          (%i1) series: 1$
          (%i2) term: exp (sin (x))$
          (%i3) for p: 1 unless p > 7 do
                    (term: diff (term, x)/p,
                     series: series + subst (x=0, term)*x^p)$
          (%i4) series;
                            7    6     5    4    2
                           x    x     x    x    x
          (%o4)            -- - --- - -- - -- + -- + x + 1
                           90   240   15   8    2

     which gives 8 terms of the Taylor series for e^sin(x).

          (%i1) poly: 0$
          (%i2) for i: 1 thru 5 do
                    for j: i step -1 thru 1 do
                        poly: poly + i*x^j$
          (%i3) poly;
                            5      4       3       2
          (%o3)          5 x  + 9 x  + 12 x  + 14 x  + 15 x
          (%i4) guess: -3.0$
          (%i5) for i: 1 thru 10 do
                    (guess: subst (guess, x, 0.5*(x + 10/x)),
                     if abs (guess^2 - 10) < 0.00005 then return (guess));
          (%o5)                  - 3.162280701754386

     This example computes the negative square root of 10 using the Newton- Raphson iteration a maximum of 10 times.  Had the convergence criterion not been met the value returned would have been done.

     Instead of always adding a quantity to the control-variable one may sometimes wish to change it in some other way for each iteration. In this case one may use next EXPRESSION instead of step INCREMENT.  This will cause the control-variable to be set to the result of evaluating EXPRESSION each time through the loop.

          (%i6) for count: 2 next 3*count thru 20 do display (count)$
                                      count = 2

                                      count = 6

                                     count = 18

     As an alternative to for VARIABLE: VALUE ...do... the syntax for VARIABLE from VALUE ...do...  may be used.  This permits the from VALUE to be placed after the step or next value or after the termination condition. If from VALUE is omitted then 1 is used as the initial value.

     Sometimes one may be interested in performing an iteration where the control-variable is never actually used.  It is thus permissible to give only the termination conditions omitting the initialization and updating information as in the following example to compute the square-root of 5 using a poor initial guess.

          (%i1) x: 1000$
          (%i2) thru 20 do x: 0.5*(x + 5.0/x)$
          (%i3) x;
          (%o3)                   2.23606797749979
          (%i4) sqrt(5), numer;
          (%o4)                   2.23606797749979

     If it is desired one may even omit the termination conditions entirely and just give do BODY which will continue to evaluate the BODY indefinitely.  In this case the function return should be used to terminate execution of the do.

          (%i1) newton (f, x):= ([y, df, dfx], df: diff (f ('x), 'x),
                    do (y: ev(df), x: x - f(x)/y,
                        if abs (f (x)) < 5e-6 then return (x)))$
          (%i2) sqr (x) := x^2 - 5.0$
          (%i3) newton (sqr, 1000);
          (%o3)                   2.236068027062195

     (Note that return, when executed, causes the current value of x to be returned as the value of the do.  The block is exited and this value of the do is returned as the value of the block because the do is the last statement in the block.)

     One other form of the do is available in Maxima.  The syntax is:

          for VARIABLE in LIST END_TESTS do BODY

     The elements of LIST are any expressions which will successively be assigned to the variable on each iteration of the BODY.  The optional termination tests END_TESTS can be used to terminate execution of the do; otherwise it will terminate when the LIST is exhausted or when a return is executed in the BODY.  (In fact, list may be any non-atomic expression, and successive parts are taken.)

          (%i1)  for f in [log, rho, atan] do ldisp(f(1))$
          (%t1)                                  0
          (%t2)                                rho(1)
                                               %pi
          (%t3)                                 ---
                                                4
          (%i4) ev(%t3,numer);
          (%o4)                             0.78539816

     Programming

 -- Function: errcatch (EXPR_1, ..., EXPR_N)

     Evaluates EXPR_1, ..., EXPR_N one by one and returns [EXPR_N] (a list) if no error occurs.  If an error occurs in the evaluation of any argument, errcatch prevents the error from propagating and returns the empty list [] without evaluating any more arguments.

     errcatch is useful in batch files where one suspects an error might occur which would terminate the batch if the error weren't caught.

     Programming

 -- Function: error (EXPR_1, ..., EXPR_N)
 -- System variable: error

     Evaluates and prints EXPR_1, ..., EXPR_N, and then causes an error return to top level Maxima or to the nearest enclosing errcatch.

     The variable error is set to a list describing the error. The first element of error is a format string, which merges all the strings among the arguments EXPR_1, ..., EXPR_N, and the remaining elements are the values of any non-string arguments.

     errormsg() formats and prints error. This is effectively reprinting the most recent error message.

     Programming

 -- Option variable: error_size
     Default value: 10

     error_size modifies error messages according to the size of expressions which appear in them.  If the size of an expression (as determined by the Lisp function ERROR-SIZE) is greater than error_size, the expression is replaced in the message by a symbol, and the symbol is assigned the expression. The symbols are taken from the list error_syms.

     Otherwise, the expression is smaller than error_size, and the expression is displayed in the message.

     See also error and error_syms.

     Example:

     The size of U, as determined by ERROR-SIZE, is 24.

          (%i1) U: (C^D^E + B + A)/(cos(X-1) + 1)$

          (%i2) error_size: 20$

          (%i3) error ("Example expression is", U);

          Example expression is errexp1
           -- an error.  Quitting.  To debug this try debugmode(true);
          (%i4) errexp1;
                                      E
                                     D
                                    C   + B + A
          (%o4)                    --------------
                                   cos(X - 1) + 1
          (%i5) error_size: 30$

          (%i6) error ("Example expression is", U);

                                   E
                                  D
                                 C   + B + A
          Example expression is --------------
                                cos(X - 1) + 1
           -- an error.  Quitting.  To debug this try debugmode(true);

     Debugging Display flags and variables

 -- Option variable: error_syms
     Default value: [errexp1, errexp2, errexp3]

     In error messages, expressions larger than error_size are replaced by symbols, and the symbols are set to the expressions.  The symbols are taken from the list error_syms.  The first too-large expression is replaced by error_syms[1], the second by error_syms[2], and so on.

     If there are more too-large expressions than there are elements of error_syms, symbols are constructed automatically, with the N-th symbol equivalent to concat ('errexp, N).

     See also error and error_size.

     Debugging Display flags and variables

 -- Function: errormsg ()

     Reprints the most recent error message. The variable error holds the message, and errormsg formats and prints it.

     Programming

 -- Option variable: errormsg
     Default value: true

     When false the output of error messages is suppressed.

     The option variable errormsg can not be set in a block to a local value.  The global value of errormsg is always present.

          (%i1) errormsg;
          (%o1)                                true
          (%i2) sin(a,b);
          Wrong number of arguments to sin
           -- an error. To debug this try: debugmode(true);
          (%i3) errormsg:false;
          (%o3)                                false
          (%i4) sin(a,b);

           -- an error. To debug this try: debugmode(true);

     The option variable errormsg can not be set in a block to a local value.

          (%i1) f(bool):=block([errormsg:bool],
                               print ("value of errormsg is",errormsg))$
          (%i2) errormsg:true;
          (%o2)                                true
          (%i3) f(false);
          value of errormsg is true
          (%o3)                                true
          (%i4) errormsg:false;
          (%o4)                                false
          (%i5) f(true);
          value of errormsg is false
          (%o5)                                false

     Programming

 -- Special operator: for

     Used in iterations.  See do for a description of Maxima's iteration facilities.

     Programming

 -- Function: go (TAG)

     is used within a block to transfer control to the statement of the block which is tagged with the argument to go.  To tag a statement, precede it by an atomic argument as another statement in the block.  For example:

          block ([x], x:1, loop, x+1, ..., go(loop), ...)

     The argument to go must be the name of a tag appearing in the same block.  One cannot use go to transfer to tag in a block other than the one containing the go.

     Programming

 -- Special operator: if

     Represents conditional evaluation.  Various forms of if expressions are recognized.

     if COND_1 then EXPR_1 else EXPR_0 evaluates to EXPR_1 if COND_1 evaluates to true, otherwise the expression evaluates to EXPR_0.

     The command if COND_1 then EXPR_1 elseif COND_2 then EXPR_2 elseif ... else EXPR_0 evaluates to EXPR_K if COND_K is true and all preceding conditions are false.  If none of the conditions are true, the expression evaluates to expr_0.

     A trailing else false is assumed if else is missing.  That is, the command if COND_1 then EXPR_1 is equivalent to if COND_1 then EXPR_1 else false, and the command if COND_1 then EXPR_1 elseif ... elseif COND_N then EXPR_N is equivalent to if COND_1 then EXPR_1 elseif ... elseif COND_N then EXPR_N else false.

     The alternatives EXPR_0, ..., EXPR_N may be any Maxima expressions, including nested if expressions.  The alternatives are neither simplified nor evaluated unless the corresponding condition is true.

     The conditions COND_1, ..., COND_N are expressions which potentially or actually evaluate to true or false. When a condition does not actually evaluate to true or false, the behavior of if is governed by the global flag prederror. When prederror is true, it is an error if any evaluated condition does not evaluate to true or false.  Otherwise, conditions which do not evaluate to true or false are accepted, and the result is a conditional expression.

     Among other elements, conditions may comprise relational and logical operators as follows.

          Operation            Symbol      Type

          less than            <           relational infix
          less than            <=
            or equal to                    relational infix
          equality (syntactic) =           relational infix
          negation of =        #           relational infix
          equality (value)     equal       relational function
          negation of equal    notequal    relational function
          greater than         >=
            or equal to                    relational infix
          greater than         >           relational infix
          and                  and         logical infix
          or                   or          logical infix
          not                  not         logical prefix

     Programming Predicate functions

 -- Function: map (F, EXPR_1, ..., EXPR_N)

     Returns an expression whose leading operator is the same as that of the expressions EXPR_1, ..., EXPR_N but whose subparts are the results of applying F to the corresponding subparts of the expressions. F is either the name of a function of n arguments or is a lambda form of n arguments.

     maperror - if false will cause all of the mapping functions to (1) stop when they finish going down the shortest EXPR_I if not all of the EXPR_I are of the same length and (2) apply F to [EXPR_1, EXPR_2, ...]  if the EXPR_I are not all the same type of object.  If maperror is true then an error message will be given in the above two instances.

     One of the uses of this function is to map a function (e.g. partfrac) onto each term of a very large expression where it ordinarily wouldn't be possible to use the function on the entire expression due to an exhaustion of list storage space in the course of the computation.

          (%i1) map(f,x+a*y+b*z);
          (%o1)                        f(b z) + f(a y) + f(x)
          (%i2) map(lambda([u],partfrac(u,x)),x+1/(x^3+4*x^2+5*x+2));
                                     1       1        1
          (%o2)                     ----- - ----- + -------- + x
                                   x + 2   x + 1          2
                                                   (x + 1)
          (%i3) map(ratsimp, x/(x^2+x)+(y^2+y)/y);
                                                1
          (%o3)                            y + ----- + 1
                                              x + 1
          (%i4) map("=",[a,b],[-0.5,3]);
          (%o4)                          [a = - 0.5, b = 3]

     Function application

 -- Function: mapatom (EXPR)

     Returns true if and only if EXPR is treated by the mapping routines as an atom.  "Mapatoms" are atoms, numbers (including rational numbers), and subscripted variables.

     Predicate functions

 -- Option variable: maperror
     Default value: true

     When maperror is false, causes all of the mapping functions, for example

          map (F, EXPR_1, EXPR_2, ...)

     to (1) stop when they finish going down the shortest EXPR_I if not all of the EXPR_I are of the same length and (2) apply F to [EXPR_1, EXPR_2, ...] if the EXPR_I are not all the same type of object.

     If maperror is true then an error message is displayed in the above two instances.

     Function application

 -- Option variable: mapprint
     Default value: true

     When mapprint is true, various information messages from map, mapl, and fullmap are produced in certain situations.  These include situations where map would use apply, or map is truncating on the shortest list.

     If mapprint is false, these messages are suppressed.

     Function application

 -- Function: maplist (F, EXPR_1, ..., EXPR_N)

     Returns a list of the applications of F to the parts of the expressions EXPR_1, ..., EXPR_N.  F is the name of a function, or a lambda expression.

     maplist differs from map(F, EXPR_1, ..., EXPR_N) which returns an expression with the same main operator as EXPR_I has (except for simplifications and the case where map does an apply).

     Function application

 -- Option variable: prederror
     Default value: false

     When prederror is true, an error message is displayed whenever the predicate of an if statement or an is function fails to evaluate to either true or false.

     If false, unknown is returned instead in this case.  The prederror: false mode is not supported in translated code; however, maybe is supported in translated code.

     See also is and maybe.

     Programming Predicate functions

 -- Function: return (VALUE)
     May be used to exit explicitly from a block, bringing its argument.  See block for more information.

     Programming

 -- Function: scanmap (F, EXPR)
 -- Function: scanmap (F, EXPR, bottomup)
     Recursively applies F to EXPR, in a top down manner.  This is most useful when complete factorization is desired, for example:

          (%i1) exp:(a^2+2*a+1)*y + x^2$
          (%i2) scanmap(factor,exp);
                                              2      2
          (%o2)                         (a + 1)  y + x

     Note the way in which scanmap applies the given function factor to the constituent subexpressions of EXPR; if another form of EXPR is presented to scanmap then the result may be different.  Thus, %o2 is not recovered when scanmap is applied to the expanded form of exp:

          (%i3) scanmap(factor,expand(exp));
                                     2                  2
          (%o3)                      a  y + 2 a y + y + x

     Here is another example of the way in which scanmap recursively applies a given function to all subexpressions, including exponents:

          (%i4) expr : u*v^(a*x+b) + c$
          (%i5) scanmap('f, expr);
                              f(f(f(a) f(x)) + f(b))
          (%o5) f(f(f(u) f(f(v)                      )) + f(c))

     scanmap (F, EXPR, bottomup) applies F to EXPR in a bottom-up manner.  E.g., for undefined f,

          scanmap(f,a*x+b) ->
             f(a*x+b) -> f(f(a*x)+f(b)) -> f(f(f(a)*f(x))+f(b))
          scanmap(f,a*x+b,bottomup) -> f(a)*f(x)+f(b)
              -> f(f(a)*f(x))+f(b) ->
               f(f(f(a)*f(x))+f(b))

     In this case, you get the same answer both ways.

     Function application

 -- Function: throw (EXPR)

     Evaluates EXPR and throws the value back to the most recent catch.  throw is used with catch as a nonlocal return mechanism.

     Programming

 -- Special operator: while
 -- Special operator: unless
     See do.

     Programming

 -- Function: outermap (F, A_1, ..., A_N)

     Applies the function F to each one of the elements of the outer product A_1 cross A_2 ... cross A_N.

     F is the name of a function of n arguments or a lambda expression of n arguments.  Each argument A_K may be a list or nested list, or a matrix, or any other kind of expression.

     The outermap return value is a nested structure.  Let X be the return value.  Then X has the same structure as the first list, nested list, or matrix argument, X[i_1]...[i_m] has the same structure as the second list, nested list, or matrix argument, X[i_1]...[i_m][j_1]...[j_n] has the same structure as the third list, nested list, or matrix argument, and so on, where M, N, ... are the numbers of indices required to access the elements of each argument (one for a list, two for a matrix, one or more for a nested list). Arguments which are not lists or matrices have no effect on the structure of the return value.

     Note that the effect of outermap is different from that of applying F to each one of the elements of the outer product returned by cartesian_product.  outermap preserves the structure of the arguments in the return value, while cartesian_product does not.

     outermap evaluates its arguments.

     See also map, maplist, and apply.

     Examples:

     Elementary examples of outermap. To show the argument combinations more clearly, F is left undefined.

          (%i1) outermap(F, [a, b, c], [1, 2, 3]);
          (%o1) [[F(a, 1), F(a, 2), F(a, 3)], [F(b, 1), F(b, 2), F(b, 3)],
                                               [F(c, 1), F(c, 2), F(c, 3)]]
          (%i2) outermap(F, matrix([a, b],[c, d]), matrix([1, 2],[3, 4]));
                   [ [ F(a, 1)  F(a, 2) ]  [ F(b, 1)  F(b, 2) ] ]
                   [ [                  ]  [                  ] ]
                   [ [ F(a, 3)  F(a, 4) ]  [ F(b, 3)  F(b, 4) ] ]
          (%o2)    [                                            ]
                   [ [ F(c, 1)  F(c, 2) ]  [ F(d, 1)  F(d, 2) ] ]
                   [ [                  ]  [                  ] ]
                   [ [ F(c, 3)  F(c, 4) ]  [ F(d, 3)  F(d, 4) ] ]
          (%i3) outermap (F, [a, b], x, matrix ([1, 2], [3, 4]));
                 [ F(a, x, 1)  F(a, x, 2) ]  [ F(b, x, 1)  F(b, x, 2) ]
          (%o3) [[                        ], [                        ]]
                 [ F(a, x, 3)  F(a, x, 4) ]  [ F(b, x, 3)  F(b, x, 4) ]
          (%i4) outermap (F, [a, b], matrix ([1, 2]), matrix ([x], [y]));
                 [ [ F(a, 1, x) ]  [ F(a, 2, x) ] ]
          (%o4) [[ [            ]  [            ] ],
                 [ [ F(a, 1, y) ]  [ F(a, 2, y) ] ]
                                        [ [ F(b, 1, x) ]  [ F(b, 2, x) ] ]
                                        [ [            ]  [            ] ]]
                                        [ [ F(b, 1, y) ]  [ F(b, 2, y) ] ]
          (%i5) outermap ("+", [a, b, c], [1, 2, 3]);
          (%o5) [[a + 1, a + 2, a + 3], [b + 1, b + 2, b + 3],
                                                     [c + 1, c + 2, c + 3]]

     A closer examination of the outermap return value.  The first, second, and third arguments are a matrix, a list, and a matrix, respectively. The return value is a matrix. Each element of that matrix is a list, and each element of each list is a matrix.

          (%i1) arg_1 :  matrix ([a, b], [c, d]);
                                      [ a  b ]
          (%o1)                       [      ]
                                      [ c  d ]
          (%i2) arg_2 : [11, 22];
          (%o2)                       [11, 22]
          (%i3) arg_3 : matrix ([xx, yy]);
          (%o3)                      [ xx  yy ]
          (%i4) xx_0 : outermap(lambda([x, y, z], x / y + z), arg_1,
                                                             arg_2, arg_3);
                         [  [      a        a  ]  [      a        a  ]  ]
                         [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
                         [  [      11       11 ]  [      22       22 ]  ]
          (%o4)  Col 1 = [                                              ]
                         [  [      c        c  ]  [      c        c  ]  ]
                         [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
                         [  [      11       11 ]  [      22       22 ]  ]

                           [  [      b        b  ]  [      b        b  ]  ]
                           [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
                           [  [      11       11 ]  [      22       22 ]  ]
                   Col 2 = [                                              ]
                           [  [      d        d  ]  [      d        d  ]  ]
                           [ [[ xx + --  yy + -- ], [ xx + --  yy + -- ]] ]
                           [  [      11       11 ]  [      22       22 ]  ]

          (%i5) xx_1 : xx_0 [1][1];
                     [      a        a  ]  [      a        a  ]
          (%o5)     [[ xx + --  yy + -- ], [ xx + --  yy + -- ]]
                     [      11       11 ]  [      22       22 ]
          (%i6) xx_2 : xx_0 [1][1] [1];
                                [      a        a  ]
          (%o6)                 [ xx + --  yy + -- ]
                                [      11       11 ]
          (%i7) xx_3 : xx_0 [1][1] [1] [1][1];
                                            a
          (%o7)                        xx + --
                                            11
          (%i8) [op (arg_1), op (arg_2), op (arg_3)];
          (%o8)                  [matrix, [, matrix]
          (%i9) [op (xx_0), op (xx_1), op (xx_2)];
          (%o9)                  [matrix, [, matrix]

     outermap preserves the structure of the arguments in the return value, while cartesian_product does not.

          (%i1) outermap (F, [a, b, c], [1, 2, 3]);
          (%o1) [[F(a, 1), F(a, 2), F(a, 3)], [F(b, 1), F(b, 2), F(b, 3)],
                                               [F(c, 1), F(c, 2), F(c, 3)]]
          (%i2) setify (flatten (%));
          (%o2) {F(a, 1), F(a, 2), F(a, 3), F(b, 1), F(b, 2), F(b, 3),
                                                 F(c, 1), F(c, 2), F(c, 3)}
          (%i3) map(lambda([L], apply(F, L)),
                               cartesian_product({a, b, c}, {1, 2, 3}));
          (%o3) {F(a, 1), F(a, 2), F(a, 3), F(b, 1), F(b, 2), F(b, 3),
                                                 F(c, 1), F(c, 2), F(c, 3)}
          (%i4) is (equal (%, %th (2)));
          (%o4)                         true

     Function application