(*^ ::[ Information = "This is a Mathematica Notebook file. It contains ASCII text, and can be transferred by email, ftp, or other text-file transfer utility. It should be read or edited using a copy of Mathematica or MathReader. If you received this as email, use your mail application or copy/paste to save everything from the line containing (*^ down to the line containing ^*) into a plain text file. On some systems you may have to give the file a name ending with ".ma" to allow Mathematica to recognize it as a Notebook. The line below identifies what version of Mathematica created this file, but it can be opened using any other version as well."; FrontEndVersion = "NeXT Mathematica Notebook Front End Version 2.2"; NeXTStandardFontEncoding; fontset = title, inactive, noPageBreakBelow, noPageBreakInGroup, nohscroll, preserveAspect, groupLikeTitle, center, M0, N0, O457, bold, L-5, a0, e130, 24, "Helvetica"; ; fontset = subtitle, inactive, noPageBreakBelow, noPageBreakInGroup, nohscroll, preserveAspect, groupLikeTitle, center, M0, N0, O457, L1, e6, 18, "Helvetica"; ; fontset = subsubtitle, inactive, noPageBreakBelow, noPageBreakInGroup, nohscroll, preserveAspect, groupLikeTitle, center, M0, N0, O457, italic, L1, e6, 12, "Times"; ; fontset = section, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeSection, grayBox, M17, N0, O457, bold, L1, a24, e2, 14, "Helvetica"; ; fontset = subsection, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeSection, blackBox, M14, N0, O457, bold, L1, a13, e2, 10, "Times"; ; fontset = subsubsection, inactive, noPageBreakBelow, nohscroll, preserveAspect, groupLikeSection, whiteBox, M14, N0, O457, L1, a12, 10, "Times"; ; fontset = text, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L0, a7, e2, 10, "Times"; ; fontset = smalltext, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L-3, a8, 8, "Helvetica"; ; fontset = input, noPageBreakInGroup, nowordwrap, preserveAspect, groupLikeInput, M0, N0, O457, bold, L-5, 12, "Courier"; ; fontset = output, output, inactive, noPageBreakInGroup, nowordwrap, preserveAspect, groupLikeOutput, M0, N0, O457, L-5, 12, "Courier"; ; fontset = message, inactive, noPageBreakInGroup, nowordwrap, preserveAspect, groupLikeOutput, M0, N0, O457, L1, e2, 10, "Times"; ; fontset = print, inactive, noPageBreakInGroup, nowordwrap, preserveAspect, groupLikeOutput, M0, N0, O457, L-5, 12, "Courier"; ; fontset = info, inactive, noPageBreakInGroup, nowordwrap, preserveAspect, groupLikeOutput, M0, N0, O457, B65535, L-5, 12, "Courier"; ; fontset = postscript, PostScript, formatAsPostScript, output, inactive, noPageBreakInGroup, nowordwrap, preserveAspect, groupLikeGraphics, M0, N0, O457, l34, w282, h287, L1, a6, 12, "Courier"; ; fontset = name, inactive, noPageBreakInGroup, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, italic, B65535, L1, 10, "Times"; ; fontset = header, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 8, "Helvetica"; ; fontset = leftheader, inactive, M0, N0, O457, 8, "Helvetica"; ; fontset = footer, inactive, nohscroll, noKeepOnOnePage, preserveAspect, center, M0, N0, O457, L1, 9, "Helvetica"; ; fontset = leftfooter, inactive, M0, N0, O457, 9, "Helvetica"; ; fontset = help, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 10, "Times"; ; fontset = clipboard, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 12; fontset = completions, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 12, "Courier"; ; fontset = special1, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 12; fontset = special2, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 12; fontset = special3, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 12; fontset = special4, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 12; fontset = special5, inactive, nohscroll, noKeepOnOnePage, preserveAspect, M0, N0, O457, L1, 12; paletteColors = 128; showRuler; automaticGrouping; currentKernel; ] :[font = title; inactive; preserveAspect; startGroup] Mathematica's Programming Language ;[s] 4:0,0;12,1;13,2;14,3;34,-1; 4:1,22,17,Helvetica,3,24,0,0,0;1,22,17,Helvetica,1,24,0,0,0;1,22,17,Helvetica,3,24,0,0,0;1,22,17,Helvetica,1,24,0,0,0; :[font = subsubtitle; inactive; preserveAspect] Roman E. Maeder Swiss Federal Institute of Technology (ETH) :[font = section; inactive; Cclosed; preserveAspect; startGroup] Abstract :[font = text; inactive; preserveAspect] Mathematica includes a rich and powerful programming language. It combines the procedural, functional, and rule-based programming styles in a single coherent system. We describe the design decisions taken in implementing Mathematica's programming language, and compare the different programming styles in the way they present themselves in Mathematica. ;[s] 6:0,0;11,1;221,2;232,3;340,4;351,5;353,-1; 6:1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] The language is well suited to handle the major programming paradigms, including procedural and functional programming, modularization, abstract data types, term rewriting, logic programming, and object-oriented programming. We show typical uses of these paradigms, and discuss implementation issues for those that are not already part of the language. :[font = text; inactive; preserveAspect; endGroup] The material presented here is adapted from Chapter 1 of The Mathematica Programmer, AP Professional, 1994. Most of the topics mentioned here are covered in detail in the other chapters of this book. Earlier versions of this material were published in The Mathematica Journal, in a column entitled "The Mathematica Programmer". ;[s] 10:0,0;58,1;84,2;253,3;276,4;300,5;303,6;316,7;326,8;327,9;329,-1; 10:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = section; inactive; Cclosed; preserveAspect; startGroup] The Programming Language :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Procedures in Mathematica ;[s] 2:0,0;14,1;25,-1; 2:1,9,7,Times,1,10,0,0,0;1,9,7,Times,3,10,0,0,0; :[font = text; inactive; preserveAspect] Strictly speaking, there are no procedures, functions, or subroutines in Mathematica. Any definition of the form f[args]:=body is a rewrite rule. Whenever the evaluator sees an expression that matches the left side, the expression is replaced by the right side with the values of the pattern variables substituted. This mechanism corresponds closely to a procedure call of a traditional language, a similarity that is intended. ;[s] 8:0,0;73,1;84,2;113,3;115,4;119,5;122,6;126,7;427,-1; 8:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] A Typical Procedure :[font = input; preserveAspect] SplitLine[vl_] := Module[{vll, pos, linelist = {}, low, high}, vll = If[NumberQ[#], #, Indeterminate]& /@ vl; pos = Flatten[ Position[vll, Indeterminate] ]; pos = Union[ pos, {0, Length[vll]+1} ]; Do[ low = pos[[i]]+1; high = pos[[i+1]]-1; If[ low < high, AppendTo[linelist, Take[vll, {low, high}]] ], {i, 1, Length[pos]-1}]; linelist ] :[font = text; inactive; preserveAspect; endGroup] (Taken from Programming in Mathematica, Chapter 1.) ;[s] 3:0,0;12,1;38,2;51,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] A Typical Function :[font = input; preserveAspect; endGroup] RandomPoly[x_, n_] := Sum[ Random[Integer, {-10, 10}] x^i, {i, 0, n} ] :[font = subsubsection; inactive; preserveAspect; startGroup] A Typical Rewrite Rule :[font = input; preserveAspect; endGroup] log[a_ b_] := log[a] + log[b] :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Formal Parameters :[font = text; inactive; preserveAspect] The pattern variables in a procedure declaration are not local variables in the procedure (as they are in Pascal or C)! To get this behavior you can use the following: :[font = input; preserveAspect] f[x0_, y0_, z0_] := Module[{x=x0, y=y0, z=z0}, (* initialized local variables *) . . x = ... (* use of local variable *) ] :[font = text; inactive; preserveAspect] On the other hand, parameter passing by reference (var parameters in Pascal, pointers in C) is also possible ;[s] 3:0,0;51,1;54,2;108,-1; 3:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] SetAttributes[f, HoldAll] :[font = input; preserveAspect; endGroup; endGroup] f[xref_Symbol] := Module[{locals...}, . . xref = val (* assignment to global parameter *) ] :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Pattern Matching and Term Rewriting :[font = text; inactive; preserveAspect] Pattern matching and term rewriting is the fundamental operating principle of Mathematica's evaluator. All other programming constructs are implemented by way of term rewriting. ;[s] 3:0,0;78,1;89,2;177,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] It is especially useful for implementing rules corresponding to transformations from a handbook of formulas. Equations in a handbook are usually meant to be used as rewrite rules, transforming the left-hand side into the right-hand side. By looking up an expression, we perform essentially pattern matching in our head. :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Example: Laplace Transforms :[font = text; inactive; preserveAspect] Definitions taken almost verbatim from a handbook of mathematics follow: :[font = input; preserveAspect] Laplace[c_, t_, s_] := c/s /; FreeQ[c, t] :[font = input; preserveAspect] Laplace[a_ + b_, t_, s_] := Laplace[a, t, s] + Laplace[b, t, s] :[font = input; preserveAspect] Laplace[c_ a_, t_, s_] := c Laplace[a, t, s] /; FreeQ[c, t] :[font = input; preserveAspect] Laplace[t_^n_., t_, s_] := n!/s^(n+1) /; (FreeQ[n, t] && n > 0) :[font = input; preserveAspect] Laplace[a_ t_^n_., t_, s_] := (-1)^n D[Laplace[a, t, s], {s, n}] /; (FreeQ[n, t] && n > 0) :[font = input; preserveAspect] Laplace[a_. Exp[b_. + c_. t_], t_, s_] := Laplace[a Exp[b], t, s-c] /; FreeQ[{b, c}, t] :[font = text; inactive; preserveAspect] Usage: :[font = input; preserveAspect; startGroup] Laplace[ Exp[omega t + phi] t^2, t, s] :[font = output; output; inactive; preserveAspect; endGroup; endGroup; endGroup; endGroup] (2*E^phi)/(-omega + s)^3 ;[o] phi 2 E ------------- 3 (-omega + s) :[font = section; inactive; Cclosed; preserveAspect; startGroup] Programming Paradigms :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Functional Programming :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Structured Iteration and Reduction :[font = text; inactive; preserveAspect] Given the problem of adding the square roots of the first 500 integers, the solution in most programming languages is to use an auxiliary variable that is incremented by the square root of a loop index iterating from 1 to 500. :[font = input; preserveAspect] sum = 0.0; Do[ sum = sum + N[Sqrt[i]], {i, 1, 500} ]; sum :[font = text; inactive; preserveAspect] In Mathematica, this loop reduces to a single statement that corresponds directly to the mathematical formula. ;[s] 3:0,0;3,1;14,2;111,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] Sum[ N[Sqrt[i]], {i, 1, 500} ] :[font = text; inactive; preserveAspect] Mathematica does not force you to think about how to implement a summation, but lets you focus instead on the concept itself. Product[] works in the same way, multiplying its terms together instead of adding them up. ;[s] 4:0,0;11,1;126,2;135,3;217,-1; 4:1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] Think of the loop body as a function applied to a list/sum/product of values. :[font = input; preserveAspect] l = {1, 2, 3, 4, 5}; :[font = input; preserveAspect; startGroup] Table[l[[i]]^2, {i, 1, Length[l]}] :[font = output; output; inactive; preserveAspect; endGroup] {1, 4, 9, 16, 25} ;[o] {1, 4, 9, 16, 25} :[font = input; preserveAspect; startGroup] #^2& /@ l :[font = output; output; inactive; preserveAspect; endGroup] {1, 4, 9, 16, 25} ;[o] {1, 4, 9, 16, 25} :[font = text; inactive; preserveAspect; endGroup] Consider this programming style whenever you see loops that iterate over a structure (list, matrix, ...). :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Folding :[font = text; inactive; preserveAspect] What does Fold[] do? One easy way to see how such a function works is to apply it to purely symbolic (undefined) functions. ;[s] 3:0,0;10,1;16,2;123,-1; 3:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect; startGroup] Fold[f, e0, {e1, e2, e3}] :[font = output; output; inactive; preserveAspect; endGroup] f[f[f[e0, e1], e2], e3] ;[o] f[f[f[e0, e1], e2], e3] :[font = text; inactive; preserveAspect] One use of Fold[] is for recursion-removal. The body of a recursively defined function (with one recursive call) can be thought of as a function f of two arguments, the result of the recursive call and the value of the recursion parameter (the value that changes from one call to the next). We then generate a list of all values of the recursion parameter and use Fold[] with the initial value equal to the value of the boundary case. Here is the standard example, factorial numbers. This is the recursive definition: ;[s] 9:0,0;11,1;17,2;25,3;42,4;145,5;146,6;364,7;370,8;517,-1; 9:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] factorial1[0] = 1; factorial1[n_] := n factorial1[n-1] :[font = text; inactive; preserveAspect] The body function is simply multiplication, the recursion parameter ranges from 1 to n. This gives the following definition: ;[s] 5:0,0;28,1;42,2;85,3;86,4;124,-1; 5:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] factorial2[n_] := Fold[ Times, 1, Range[1, n] ] :[font = text; inactive; preserveAspect] Note that it works also for n = 0. ;[s] 3:0,0;28,1;29,2;34,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] FoldList[] shows the intermediate steps. ;[s] 2:0,0;10,1;41,-1; 2:1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect; startGroup] FoldList[ Times, 1, Range[1, 10] ] :[font = output; output; inactive; preserveAspect; endGroup; endGroup] {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800} ;[o] {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800} :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Nesting and Fixed Points :[font = text; inactive; preserveAspect] Loops that compute a value iteratively are examples of nested function application. :[font = input; preserveAspect] r = 2.0; :[font = input; preserveAspect; startGroup] x = 1.0; Do[ x = (x +r/x)/2, {10}]; x :[font = output; output; inactive; preserveAspect; endGroup] 1.414213562373095 ;[o] 1.41421 :[font = input; preserveAspect; startGroup] Nest[Function[x, (x +r/x)/2], 1.0, 10] :[font = output; output; inactive; preserveAspect; endGroup] 1.414213562373095 ;[o] 1.41421 :[font = input; preserveAspect; startGroup] FixedPoint[Function[x, (x +r/x)/2], 1.0] :[font = output; output; inactive; preserveAspect; endGroup; endGroup; endGroup] 1.414213562373095 ;[o] 1.41421 :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Logic Programming :[font = text; inactive; preserveAspect] Reversing a list in typical Prolog style. :[font = input; preserveAspect] reverse[l_] := rev[ l, {} ] rev[ {}, r_ ] := r rev[ {h_, t___}, {r___} ] := rev[ {t}, {h, r} ] :[font = input; preserveAspect; startGroup] reverse[ {a, b, c} ] :[font = output; output; inactive; preserveAspect; endGroup] {c, b, a} ;[o] {c, b, a} :[font = text; inactive; preserveAspect] Backtracking can be implemented with side conditions. :[font = input; preserveAspect] sort[ {alpha___, x_, y_, omega___} ] := sort[ {alpha, y, x, omega} ] /; x > y :[font = input; preserveAspect] sort[ l_ ] := l :[font = input; preserveAspect; startGroup] sort[ {5, 1, 3, 2} ] :[font = output; output; inactive; preserveAspect; endGroup] {1, 2, 3, 5} ;[o] {1, 2, 3, 5} :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Prolog :[font = text; inactive; preserveAspect] Here is a Prolog interpreter written in Mathematica. ;[s] 3:0,0;40,1;51,2;52,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] Needs["LogicProgramming`"] :[font = text; inactive; preserveAspect] This is the game of NIM. :[font = input; preserveAspect] < 1 && Var[n1] == -1 + Var[n]], rule[play[cons[Var[n], Var[rest]], take[1, Var[m]], cons[Var[nr], Var[rest]]], Var[n] > 2 && Var[n1] == -1 + Var[n] && play[cons[Var[n1], Var[rest]], take[1, Var[m1]], cons[Var[nr], Var[rest]]] && Var[m] == 1 + Var[m1]], rule[play[cons[Var[n], Var[rest]], take[Var[i], Var[m]], cons[Var[n], Var[rest1]]], play[Var[rest], take[Var[j], Var[m]], Var[rest1]] && Var[i] == 1 + Var[j]]} ;[o] {play[(n_ . rest_), take[1, n_], rest_], play[(n_ . rest_), take[1, 1], (n1_ . rest_)] :- ((n_) > 1 && (n1_) == -1 + (n_)), play[(n_ . rest_), take[1, m_], (nr_ . rest_)] :- ((n_) > 2 && (n1_) == -1 + (n_) && play[(n1_ . rest_), take[1, m1_], (nr_ . rest_)] && (m_) == 1 + (m1_)), play[(n_ . rest_), take[i_, m_], (n_ . rest1_)] :- (play[rest_, take[j_, m_], rest1_] && (i_) == 1 + (j_))} :[font = text; inactive; preserveAspect] The strategy of NIM follows: :[font = input; preserveAspect; startGroup] LogicValues[win] :[font = output; output; inactive; preserveAspect; endGroup] {rule[win[Var[pos], Var[t]], play[Var[pos], Var[t], Var[pos1]] && !win[Var[pos1], Var[$, 0]]]} ;[o] {win[pos_, t_] :- (play[pos_, t_, pos1_] && !win[pos1_, _])} :[font = text; inactive; preserveAspect] All moves from position (1 2 1) are given. ;[s] 3:0,0;24,1;31,2;42,-1; 3:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect; startGroup] QueryAll[play[list[1,2,1], t_, _]] :[font = print; inactive; preserveAspect; endGroup] {t -> take[1, 1]} {t -> take[2, 2]} {t -> take[2, 1]} {t -> take[3, 1]} :[font = text; inactive; preserveAspect] Find the winning move. :[font = input; preserveAspect; startGroup] Query[win[list[1,2,1], t_]] :[font = output; output; inactive; preserveAspect; endGroup] {t -> take[2, 2]} ;[o] {t -> take[2, 2]} :[font = text; inactive; preserveAspect] Perform this move. :[font = input; preserveAspect; startGroup] Query[play[list[1,2,1], t /. %, newpos_]] :[font = output; output; inactive; preserveAspect; endGroup] {newpos -> cons[1, cons[1, nil]]} ;[o] {newpos -> (1 1)} :[font = text; inactive; preserveAspect; plain; italic; fontName = "Palatino"; endGroup; endGroup] See The Mathematica Journal, Vol. 4, issues 1 and 2. :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Modularization :[font = text; inactive; preserveAspect] Good programming style suggests separating definition and implementation. The exported functions are declared and documented. Any needed functions are declared and imported. The package mechanism in Mathematica provides for these features. It is explained in detail in Programming in Mathematica (Addison-Wesley, 1991). ;[s] 5:0,0;199,1;210,2;269,3;295,4;319,-1; 5:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] A Demonstration Package :[font = text; inactive; preserveAspect] Create a clean state for a new package and declare any imports. :[font = input; preserveAspect] BeginPackage["Demo`", "Needed1`"] :[font = text; inactive; preserveAspect] Create the public symbols. This is best done with a usage message. :[font = input; preserveAspect] SampleFunction::usage = "SampleFunction[n] does nothing" :[font = text; inactive; preserveAspect] Start the implementation part. :[font = input; preserveAspect] Begin["`Private`"] :[font = text; inactive; preserveAspect] All new symbols in this part will not be exported. :[font = input; preserveAspect] SampleFunction[n_] := n^3 + 2n^2 + 3n + 4 :[font = input; preserveAspect] AuxFunction[m_] := something :[font = input; preserveAspect] End[] :[font = text; inactive; preserveAspect] End the package and add its context to $ContextPath. ;[s] 2:0,0;39,1;63,-1; 2:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0; :[font = input; preserveAspect] EndPackage[] :[font = text; inactive; preserveAspect; blackDot] Reading it in. :[font = text; inactive; preserveAspect] This is a machine-independent way of reading in packages. :[font = input; preserveAspect] << Demo` :[font = text; inactive; preserveAspect] The exported function can be used outside. :[font = input; preserveAspect; startGroup] ?SampleFunction :[font = print; inactive; preserveAspect; endGroup] SampleFunction[n] does nothing :[font = text; inactive; preserveAspect] The private symbols are not found. :[font = input; preserveAspect; startGroup] ?AuxFunction :[font = message; inactive; preserveAspect; endGroup; endGroup] Information::notfound: Symbol AuxFunction not found. :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Import of Packages: Details :[font = text; inactive; preserveAspect; blackDot] Transitivity: if A` needs B` and B` needs C`, can A` use C`? ;[s] 14:0,0;17,1;19,2;26,3;28,4;33,5;35,6;42,7;44,8;50,9;51,10;52,11;57,12;59,13;60,-1; 14:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,1,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect; startGroup] BeginPackage["A`", "B`"]; a::usage = "a in A"; Print[$Context, $ContextPath]; EndPackage[]; :[font = print; inactive; preserveAspect; endGroup] A`{A`, B`, System`} :[font = input; inactive; preserveAspect] BeginPackage["B`", "C`"] b::usage = "b in B" EndPackage[] :[font = input; inactive; preserveAspect] BeginPackage["C`"] c::usage = "c in C" EndPackage[] :[font = text; inactive; preserveAspect] In Version 2.2, C` is not available inside the package A`. This will be fixed in the next version. ;[s] 5:0,0;16,1;18,2;55,3;57,4;99,-1; 5:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; noPageBreakBelow; preserveAspect; blackDot] Hidden import :[font = input; preserveAspect; startGroup] BeginPackage["D1`"]; Needs["E`"]; (* hidden import *) Print[$Context, $ContextPath]; EndPackage[]; :[font = print; inactive; noPageBreak; preserveAspect; endGroup] D1`{E`, D1`, System`} :[font = input; preserveAspect; startGroup] BeginPackage["D2`"]; Needs["E`"]; (* hidden import *) Print[$Context, $ContextPath]; EndPackage[]; :[font = print; inactive; noPageBreak; preserveAspect; endGroup] D2`{E`, D2`, System`} :[font = input; preserveAspect; startGroup] $Packages :[font = output; output; inactive; preserveAspect; endGroup] {"D2`", "D1`", "E`", "Global`", "System`"} ;[o] {D2`, D1`, E`, Global`, System`} :[font = input; preserveAspect; startGroup] $ContextPath :[font = output; output; inactive; preserveAspect; endGroup] {"D2`", "D1`", "Global`", "System`"} ;[o] {D2`, D1`, Global`, System`} :[font = text; inactive; preserveAspect; endGroup; endGroup] In Version 2.2.0, a second attempt at hidden import fails, since the package is not read again, and it is not put on $ContextPath. This is now fixed. ;[s] 3:0,0;117,1;129,2;149,-1; 3:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Abstract Data Types :[font = text; inactive; preserveAspect] Abstract data types are both a theoretically well-defined concept and a useful tool for program development. Following the principles of abstract data type design, one arrives at a clear separation of specification and implementation. :[font = text; inactive; preserveAspect; spaceAbove = 4] Abstract data types are defined in terms of type names, function names, and equations. These can be realized in Mathematica very easily. The equations become rewrite rules. The interactive nature of Mathematica makes it well suited for rapid prototyping and testing of designs. ;[s] 5:0,0;112,1;123,2;199,3;210,4;277,-1; 5:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] A detailed discussion of abstract data types in Mathematica can be found in The Mathematica Programmer, and in Vol. 2, No. 3 of The Mathematica Journal. Here is a short excerpt and an example. ;[s] 11:0,0;48,1;59,2;76,3;102,4;128,5;131,6;132,7;143,8;144,9;151,10;193,-1; 11:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] How to Define a Data Type :[font = text; inactive; preserveAspect; blackDot] Define the names of the sorts to be used. :[font = text; inactive; preserveAspect; blackDot] Define the constants to be used. :[font = text; inactive; preserveAspect; blackDot] Define constructors and selectors. :[font = text; inactive; preserveAspect; blackDot; endGroup] Write down the equations that should hold. :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] How to implement a Data Type :[font = text; inactive; preserveAspect; blackDot] Choose a representation for elements of the sorts defined. Usually you can use a normal expression having the name of the sort as head. This corresponds roughly to a record in many programming languages. :[font = text; inactive; preserveAspect; blackDot] Derive rules for normal forms for the data elements. This ensures that elements are stored in a unique way. The rules are derived from the equations and can normally be put into the constructors. :[font = text; inactive; preserveAspect; blackDot] Define constructors and selectors. These are the only operations that are allowed access to the details of the internal representations. :[font = text; inactive; preserveAspect; blackDot] Define the other operations. An operation for which exactly one equation exists can usually be turned into a simple rewrite rule. (Such an operation is called a derived operation.) The variables in the left side of the rules are in the form n_type, restricting the arguments to the correct types. Use only selectors to access parts of the data elements on the right side of the rules. ;[s] 7:0,0;161,1;168,2;241,3;242,4;243,5;247,6;384,-1; 7:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,8,6,Courier,0,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect; blackDot] Use overloading where appropriate. Overloading built-in functions is most easily done by defining up-values, rules of the form :[font = input; inactive; preserveAspect] g/: f[n_g, ...] := ... ;[s] 4:0,0;11,1;14,2;19,3;22,-1; 4:1,10,8,Courier,1,12,0,0,0;1,11,8,Times,0,12,0,0,0;1,10,8,Courier,1,12,0,0,0;1,11,8,Times,0,12,0,0,0; :[font = text; inactive; preserveAspect; blackDot] Choose a suitable output representation of data elements. :[font = text; inactive; preserveAspect; blackDot; endGroup] Define automatic type conversions where useful. :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] An Example :[font = text; inactive; preserveAspect] This is a data type for modular numbers. :[font = text; inactive; preserveAspect; grayDot] Design :[font = text; inactive; preserveAspect] Sorts: mod, Integer ;[s] 2:0,0;9,1;21,-1; 2:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0; :[font = text; inactive; preserveAspect] Constants: p ;[s] 2:0,0;12,1;13,-1; 2:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0; :[font = text; inactive; preserveAspect] Constructors: makemod ;[s] 2:0,0;15,1;31,-1; 2:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0; :[font = text; inactive; preserveAspect] Selectors: rep ;[s] 2:0,0;12,1;15,-1; 2:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0; :[font = text; inactive; preserveAspect] Derived Operations: Plus, Times ;[s] 2:0,0;20,1;90,-1; 2:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0; :[font = text; inactive; preserveAspect] Equations: Mod[ rep[ mod[n] ] - n, p ] == 0 ;[s] 4:0,0;12,1;33,2;34,3;166,-1; 4:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,7,6,Courier,1,10,0,0,0; :[font = text; inactive; preserveAspect; grayDot] Implementation :[font = input; preserveAspect] makemod[n_Integer] := mod[ Mod[n, p] ] :[font = input; preserveAspect] rep[ mod[n_] ] := n :[font = input; preserveAspect] mod/: m1_mod + m2_mod := makemod[ rep[m1] + rep[m2] ] mod/: m1_mod * m2_mod := makemod[ rep[m1] * rep[m2] ] :[font = text; inactive; preserveAspect] Note that we have overloaded the built-in addition and subtraction functions. This corresponds directly to the use of the same names in the specification. ;[s] 3:0,0;18,1;28,2;154,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; noPageBreakBelow; preserveAspect; grayDot] Usage :[font = text; inactive; noPageBreakBelow; preserveAspect] This sets the modulus to 5 for the computations that follow. :[font = input; preserveAspect] p = 5; :[font = text; inactive; preserveAspect] This makes two modular numbers and assigns them to the variables m2 and m3. ;[s] 5:0,0;65,1;67,2;72,3;74,4;76,-1; 5:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] m2 = makemod[2]; m3 = makemod[3]; :[font = text; inactive; preserveAspect] Their sum is 0 (modulo 5). :[font = input; preserveAspect; startGroup] m2 + m3 :[font = output; output; inactive; preserveAspect; endGroup] mod[0] ;[o] mod[0] :[font = text; inactive; preserveAspect] Their product is 1 (modulo 5). :[font = input; preserveAspect; startGroup] m2 m3 :[font = output; output; inactive; preserveAspect; endGroup; endGroup; endGroup] mod[1] ;[o] mod[1] :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Lazy Evaluation: Infinity :[font = text; inactive; preserveAspect] With lazy evaluation, arguments of functions are only evaluated when they are needed, not when the function is called. It can be implemented with the attributes HoldFirst and HoldRest. Among other things, this idea allows us to implement infinite lists. ;[s] 5:0,0;161,1;170,2;175,3;183,4;253,-1; 5:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] It is, of course, impossible to deal with an infinite set explicitly on a finite computer. But a set whose elements can be generated one at a time, each in a finite number of steps---such a set is called recursively enumerable---can be considered in some sense as entirely "known," because any element can be produced upon demand. The set of all perfect squares, for example, is recursively enumerable, for each element can be computed by the formula n^2, where n = 1, 2,.... Such a formula describes a function of the integers; we can therefore define a recursively enumerable set as the range of a computable (or recursive) function. ;[s] 13:0,0;45,1;53,2;204,3;226,4;451,5;452,6;462,7;463,8;589,9;594,10;615,11;624,12;635,-1; 13:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] We will call our infinite lists streams (not to be confused with input and output streams in Mathematica). Good software design principles suggest that we do not work on stream objects directly, but only through an interface that hides the implementation details. Here the interface consists of a constructor MakeStream[first, rest] and two selectors First[stream] and Rest[stream]. ;[s] 22:0,0;32,1;39,2;93,3;104,4;170,5;176,6;309,7;320,8;325,9;326,10;327,11;331,12;332,13;351,14;357,15;363,16;364,17;369,18;374,19;380,20;381,21;382,-1; 22:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,1,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect] The data-structure built in this way is the same as lists in Lisp. The rest, however, is now a function that produces the rest of the stream (usually another stream) when called. Until then it is held in unevaluated form. ;[s] 3:0,0;71,1;75,2;2133,-1; 3:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,3,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] << Streams.m :[font = subsubsection; inactive; Cclosed; preserveAspect; spaceAbove = 4; startGroup] First Example :[font = text; inactive; preserveAspect] Our first example is a stream of zeroes. :[font = input; preserveAspect; startGroup] zeroes = MakeStream[0, zeroes] :[font = output; output; inactive; preserveAspect; endGroup] Stream[0, zeroes] ;[o] {0, zeroes ...} :[font = input; preserveAspect; startGroup] First[zeroes] :[font = output; output; inactive; preserveAspect; endGroup] 0 ;[o] 0 :[font = input; preserveAspect; startGroup] Rest[zeroes] :[font = output; output; inactive; preserveAspect; endGroup] Stream[0, zeroes] ;[o] {0, zeroes ...} :[font = text; inactive; preserveAspect] We can take the rest several times. :[font = input; preserveAspect; startGroup] Nest[ Rest, zeroes, 1000 ] :[font = output; output; inactive; preserveAspect; endGroup; endGroup] Stream[0, zeroes] ;[o] {0, zeroes ...} :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] The Set of Integers :[font = text; inactive; preserveAspect] The definition below produces a stream of integers starting from n. ;[s] 3:0,0;65,1;66,2;67,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] IntegersFrom[n_Integer] := MakeStream[n, IntegersFrom[n+1]] :[font = text; inactive; preserveAspect] The first element of the stream of integers from n is, of course, n itself, and the rest is the stream of integers from n+1. ;[s] 7:0,0;49,1;50,2;66,3;67,4;120,5;121,6;125,-1; 7:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect; startGroup] integers = IntegersFrom[1] :[font = output; output; inactive; preserveAspect; endGroup] Stream[1, IntegersFrom[1 + 1]] ;[o] {1, IntegersFrom[1 + 1] ...} :[font = text; inactive; preserveAspect] If we take the rest of integers 99 times and then take the first element, we should get the 100th element. We can overload Part[] to perform this operation on streams. ;[s] 5:0,0;23,1;31,2;123,3;129,4;178,-1; 5:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect; startGroup] integers[[100]] :[font = output; output; inactive; preserveAspect; endGroup; endGroup] 100 ;[o] 100 :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Streams like Lists :[font = text; inactive; preserveAspect] We can overload most functions on lists to work also with streams. :[font = text; inactive; noPageBreakBelow; preserveAspect; grayDot] Listable functions thread over streams as they do over lists. :[font = input; preserveAspect; startGroup] integers^2 :[font = output; output; inactive; preserveAspect; endGroup] Stream[1, IntegersFrom[1 + 1]^2] ;[o] 2 {1, IntegersFrom[1 + 1] ...} :[font = input; preserveAspect; spaceBelow = 2; startGroup] Take[%, 10] :[font = output; output; inactive; preserveAspect; spaceAbove = 8; endGroup] {1, 4, 9, 16, 25, 36, 49, 64, 81, 100} ;[o] {1, 4, 9, 16, 25, 36, 49, 64, 81, 100} :[font = text; inactive; noPageBreakBelow; preserveAspect; grayDot] Most functional constructs work on streams. :[font = input; preserveAspect; startGroup] Select[ integers, PrimeQ ] :[font = output; output; inactive; preserveAspect; endGroup] Stream[2, Select[IntegersFrom[2 + 1], PrimeQ]] ;[o] {2, Select[IntegersFrom[2 + 1], PrimeQ] ...} :[font = input; preserveAspect; startGroup] %[[1000]] :[font = output; output; inactive; preserveAspect; endGroup] 7919 ;[o] 7919 :[font = text; inactive; preserveAspect] Nest[] without its third argument produces a stream. ;[s] 2:0,0;6,1;53,-1; 2:1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect; startGroup] goldenstream = Nest[ Function[x, 1 + 1/x], 1 ] :[font = output; output; inactive; preserveAspect; endGroup] Stream[1, Nest[Function[x, 1 + 1/x], Function[x, 1 + 1/x][1]]] ;[o] 1 {1, Nest[Function[x, 1 + -], <<1>>] ...} x :[font = input; preserveAspect; startGroup] Take[ goldenstream, 15 ] :[font = output; output; inactive; preserveAspect; endGroup] {1, 2, 3/2, 5/3, 8/5, 13/8, 21/13, 34/21, 55/34, 89/55, 144/89, 233/144, 377/233, 610/377, 987/610} ;[o] 3 5 8 13 21 34 55 89 144 233 377 610 987 {1, 2, -, -, -, --, --, --, --, --, ---, ---, ---, ---, ---} 2 3 5 8 13 21 34 55 89 144 233 377 610 :[font = input; preserveAspect; startGroup] N[%, 10] :[font = output; output; inactive; preserveAspect; endGroup; endGroup; endGroup] {1., 2., 1.5, 1.666666666666667, 1.6, 1.625, 1.615384615384615, 1.619047619047619, 1.617647058823529, 1.618181818181818, 1.617977528089888, 1.618055555555556, 1.618025751072961, 1.618037135278514, 1.618032786885246} ;[o] {1., 2., 1.5, 1.666666667, 1.6, 1.625, 1.615384615, 1.619047619, 1.617647059, 1.618181818, 1.617977528, 1.618055556, 1.618025751, 1.618037135, 1.618032787} :[font = subsection; inactive; Cclosed; preserveAspect; startGroup] Object-oriented Programming :[font = text; inactive; preserveAspect] Object-oriented programming is a programming style that is becoming more and more popular. It promises code-reuse and easier maintenance of larger projects than is possible with traditional procedural languages. Its use of methods and message passing instead of procedure calls shifts the programmer's view towards close integration of data and operations. :[font = text; inactive; preserveAspect] An interactive object-oriented language can easily be implemented in Mathematica. The implementation is in the package Classes.m, available from MathSource. ;[s] 6:0,0;69,1;80,2;119,3;128,4;145,5;156,-1; 6:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Objects and Classes :[font = text; inactive; preserveAspect] The first important aspect of object-oriented languages is that functions are considered part of data. A data object "knows" which operations can be performed on it. The functions defined for a certain type of object are part of that object. :[font = text; inactive; preserveAspect] Thus an object is a collection of data elements and operations that act on these data elements. The operations are called methods. ;[s] 7:0,0;8,1;14,2;62,3;63,4;122,5;129,6;130,-1; 7:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = text; inactive; preserveAspect; endGroup] Methods are usually not defined for each object separately but are collected in a class. Objects then belong to a class from which they take their methods. ;[s] 3:0,0;82,1;87,2;155,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Message Passing :[font = text; inactive; preserveAspect; endGroup] A uniform mechanism, called message passing, is provided for invoking the correct piece of code, if a function is called or, as we now prefer to say, if a message is passed to an object to execute a certain method. This viewpoint emphasizes that it is the object's responsibility to react to a message. ;[s] 3:0,0;28,1;43,2;302,-1; 3:1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] Inheritance :[font = text; inactive; preserveAspect] Often a number of related data types have some common characteristics. Some operations on them can be written in a way that does not depend on which of the data types the operations are applied to. Common characteristics of related data types can then be isolated and encapsulated in a new data type. The other data types are then made subtypes of the new type. They inherit the characteristics of the common type, and need only implement those aspects in which they differ from their supertype. :[font = text; inactive; preserveAspect; endGroup] Thus, much of the code needs to be written only once. This saves development time and--perhaps more important--ensures consistency, since a change needs to be made only once, instead of being applied to several almost identical pieces of code. :[font = subsubsection; inactive; Cclosed; preserveAspect; startGroup] An Example :[font = text; inactive; preserveAspect] The implementation of the object-oriented system in the package Classes.m is described in detail in Vol. 3, No. 1 of The Mathematica Journal. An updated version of the package is available from MathSource. ;[s] 11:0,0;64,1;73,2;117,3;120,4;121,5;132,6;133,7;140,8;151,9;194,10;214,-1; 11:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0; :[font = input; preserveAspect] Needs["Classes`"] :[font = text; inactive; preserveAspect] A class is declared with :[font = input; preserveAspect] Class[ name, superclass, instance variables, methods ] ;[s] 9:0,0;7,1;11,2;13,3;23,4;25,5;43,6;45,7;52,8;55,-1; 9:1,10,8,Courier,1,12,0,0,0;1,10,8,Courier,3,12,0,0,0;1,10,8,Courier,1,12,0,0,0;1,10,8,Courier,3,12,0,0,0;1,10,8,Courier,1,12,0,0,0;1,10,8,Courier,3,12,0,0,0;1,10,8,Courier,1,12,0,0,0;1,10,8,Courier,3,12,0,0,0;1,10,8,Courier,1,12,0,0,0; :[font = text; inactive; preserveAspect] An account has one instance variable, the balance, and methods to initialize it and to deposit and withdraw money and to ask what the balance is. :[font = input; preserveAspect] Class[ Account, Object, {bal}, {{new, (new[super]; bal = #1)&}, {balance, bal&}, {deposit, Function[bal += #1]}, {withdraw, Function[bal -= #1]}, {delete, (If[ bal != 0, Print["Remaining balance:", bal]]; delete[super])&} } ]; :[font = input; preserveAspect; startGroup] a1 = new[Account, 100] :[font = output; output; inactive; preserveAspect; endGroup] Classes`Private`Account[bal$74] ;[o] -Account- :[font = text; inactive; preserveAspect] Message passing is made to look like ordinary function calls. The object that receives the message is the first argument. :[font = input; preserveAspect; startGroup] deposit[ a1, 200 ] :[font = output; output; inactive; preserveAspect; endGroup] 300 ;[o] 300 :[font = input; preserveAspect; startGroup] withdraw[ a1, 500 ] :[font = output; output; inactive; preserveAspect; endGroup] -200 ;[o] -200 :[font = text; inactive; preserveAspect] Here is a subclass of Account. It inherits most behavior. The method for withdrawing money is overridden to check whether the balance is sufficient for the withdrawal. If it is, we invoke the method in the superclass Account to perform an ordinary withdrawal. ;[s] 7:0,0;22,1;29,2;94,3;104,4;217,5;224,6;260,-1; 7:1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,9,7,Times,2,10,0,0,0;1,9,7,Times,0,10,0,0,0;1,7,6,Courier,1,10,0,0,0;1,9,7,Times,0,10,0,0,0; :[font = input; preserveAspect] Class[ NoCreditAccount, Account, {}, {{withdraw, Function[ If[ balance[self] < #1, Print["cannot withdraw ", #1], withdraw[super, #1] ] ]} } ]; :[font = input; preserveAspect; startGroup] a2 = new[NoCreditAccount, 1000] :[font = output; output; inactive; preserveAspect; endGroup] Classes`Private`NoCreditAccount[bal$77] ;[o] -NoCreditAccount- :[font = input; preserveAspect; startGroup] withdraw[ a2, 900 ] :[font = output; output; inactive; preserveAspect; endGroup] 100 ;[o] 100 :[font = input; preserveAspect; startGroup] withdraw[ a2, 110 ] :[font = print; inactive; preserveAspect; endGroup] cannot withdraw 110 :[font = input; preserveAspect; startGroup] balance[ a2] :[font = output; output; inactive; preserveAspect; endGroup; endGroup; endGroup; endGroup; endGroup] 100 ;[o] 100 ^*)