Contexts and Packages

Mathematica provides some convenient features for organizing larger pieces of program code. Topics to be addressed when developing bigger software are for example:

name collisions,

loading programs from external files,

separation between public parts of the software and private auxiliary parts of the software,

dependencies between several blocks of code,

automatic loading of software components.

All the topics listed above finally lead to the package concept of Mathematica, which we will introduce in this chapter. As a prerequisite for Mathematica packages, we need to understand the concept of contexts in the Mathematica environment.

Contexts

Names for symbols are organized in Mathematica in so–called contexts. The fullname of every Mathematica symbol consists of a shortname and its context written as context`shortname.

newContext`x=3

newContext`x

When accessing a symbol, one should access it through its fullname. However, the explicit specification of a context is optional. If a symbol is specified without an explicit context, then it is assumed to be in the current context.

x

x=4

x==newContext`x

In the example, the symbol x occurs twice:

The x on the left–hand side is taken in the current context, its value is 4.

The x on the right–hand side is taken in the context newContext, its value is 3.

The current context is always available in the global variable $Context.

$Context

Global`x

"Global`" is the default context, into which the Mathematica kernel goes on startup. As shown already above, symbols in the current context can be specified by their shortname only or by their fullname. (Compare the concept of shortnames and contexts to the concept of phone numbers and area code!)

Global`x==x

Contexts are even more powerful: Not only symbols in the current context can be accessed with their shortname only!

N[π]

although

Context[π]

Context[N]

This is because when a symbol is accessed by its shortname it is searched for first in the current context and then in all contexts listed in the global variable $ContextPath.

$ContextPath

x

Context[x]

(Compare this to the concept of pathnames in an operating system!)

In order to change the current context use Begin.

Begin["newContext`"];

$Context

$ContextPath

x

For every symbol, you can ask its context. Note, of course, that the context of a symbol is not an absolute notion but it always depends on the current setting of $Context and $ContextPath (see the context of x above!).

Context[x]

Using

End[];

we can leave the current context and return to its previous value. Begin and End maintain a context stack.

$Context

x

Context[x]

If a symbol is used for the first time in a Mathematica session and it is found neither in the current context nor in any context in the context path, then it will e created in the current context.

Packages

Mathematica program packages are realized by just maintaining current context and context path in a suitable fashion. First, let's quickly summarize what we expect from packing certain parts of program code into a program package.

A package should have a clear interface, i.e. a collection of symbols (usually functions) that the package provides for the user.

A package should have a clear separation between functions it provides for the user and auxiliary functions, which are not intended to be called directly by the user. (Compare to a class in C++ having public and private members.)

A package should be loadable into the current Mathematica session from an external file.

Values in the current Mathematica session should not interfere with values needed in the package.

The mechanism for setting up a Mathematica package named "MyPackage" is as follows:

BeginPackage["MyPackage`"];

Note that the only thing that happens is the manipulation of context and context path.

$Context

$ContextPath

In this stage, the default context "Global`" is not available, which separates the package completely from all symbols created in the standard Mathematica session! If we use a new symbol in this stage it will be created in the package's own context.

ExportFun

Context[ExportFun]

An exported symbol in a package is a symbol that is visible also outside the package so that it is accessible by the user of the package. In order to create the symbols for all functions to be exported form the package in the package's context, all these symbols mus be used in this stage at least once. A common standard is to use the symbol by defining a usage–message for it.

ExportFun1::usage="ExportFun1[n] is a very useful function. The parameter n bla bla";

Don't yet give the definitions for the functions and don't use any symbols that should be kept private in the package. After having used all exported functions, change to a new subcontext (note the ` at the beginning of the name). The name of the new subcontext does not matter, it is however common to call it "`Private`".

Begin["`Private`"];

Probably you can imagine what happened:

$Context

$ContextPath

Now comes the entire program code making up the package.

ExportFun1[n_]:=Factor[AuxiliaryFun1[n]]

AuxiliaryFun1[n_]:=Expand[n]

when everything is finished, first leave the private context

End[];

$Context

$ContextPath

and finally leave the package context.

EndPackage[]

$Context

$ContextPath

Hence, symbols in the package context can be accessed directly by their shortnames.

?Solve

?ExportFun1

?ImplicitPlot

$ContextPath

?VectorFieldPlot

Context[VectorFieldPlot]

Needs["VectorFieldPlots`"]

$ContextPath

Needs["Graphics`ImplicitPlot`"]

Remove[x]

ImplicitPlot[(x^2)+(y^2)==1,{x,-1,1}]

Context[VectorFieldPlot]

Remove[VectorFieldPlot]

Context[VectorFieldPlot]

Remove[ExportFun1]

Context[ExportFun1]

?ExportFun1

??ExportFun1

?AuxiliaryFun1

?MyPackage`Private`AuxiliaryFun1

ExportFun1[(((z+1))^2)]

AuxiliaryFun1[(((z+1))^2)]

MyPackage`Private`AuxiliaryFun1[(((z+1))^2)]

Usually the entire code of a package is stored in a separate file so that it can be loaded into a Mathematica session using Get.

<<file

However, loading a package can be made much more comfortable using Needs. Some conventions have to be followed with respect to the name of the package, the name of the file, and the location of the package file in the file system in order to exploit all the power of Needs.
Suppose we have a package named "MyPackage", i.e. the main context of the package is "MyPackage`". Make sure to save the package in file named "MyPackage.m" and store it in one of the directories in $Path.

$Path

The directories in this list are contained in their expanded form. Note that system–independent and Mathematica version independent locations can be found with the help of certain global variables such as $UserBaseDirectory, $BaseDirectory, or $InstallationDirectory.

$UserBaseDirectory

$UserAddOnsDirectory

$BaseDirectory

$InstallationDirectory

$Path

As long as these conventions are followed, the package can be loaded simply by

Needs["MyPackage`"]

and the code will actually only be loaded if it has not already been load into the current session.

$Packages

If the file is physically located in a different location then $Path needs to be adapted. If the package "MyPackage" is intended to be stored in a subdirectory named "SubDir" of a directory in $Path, then the name of the package must be "SubDir`MyPackage`", and you can immediately load it using

Needs["SubDir`MyPackage`"]

The typical structure of a package is:

BeginPackage["..."]
	one usage-message per exported symbol
Begin["`Private`"]
	implementation of the whole package, both imported and exported symbols
End[ ]
EndPackage[ ]

$Packages

Dependencies between Packages

As we see from the mechanism above, when entering a package's context using BeginPackage, only the package's context and "System`" are in the context path. As a consequence, symbols from other packages cannot be used inside the package unless they are specified with their full names. This is inconvenient! In such a situation, we use

BeginPackage["MyPackage`",{"Pack1`","Pack2`"}]

$ContextPath

We see that these context will be available inside the package. Moreover, the packages "Pack1" and "Pack2" will automatically be loaded before the code of "MyPackage" is evaluated.

EndPackage[]

In this case the package structure is:

BeginPackage["...", {dependencies}]
	one usage-message per exported symbol
Begin["`Private`"]
	implementation of the whole package, both imported and exported symbols
End[ ]
EndPackage[ ]

A minor disadvantage of this method is that if we have many packages in the "dependencies" then the context path becomes "messed up". If, at the same time, we want to use functions provided by these packages directly then there is probably no other way than building up a long context path. If, however, the "dependencies" are mainly auxiliary for the package but they are not required on the "top–level" then it might be preferable to keep the context path short and clean. In this case, use the following package skeleton:

BeginPackage["..."]
	one usage-message per exported symbol
Begin["`Private`"]
	Needs["Pack1`"];
	Needs["Pack2`"];
	…
	implementation of the whole package, both imported and exported symbols
End[ ]
EndPackage[ ]

Automatic Loading of Packages

[Kill the local kernel before you proceed]

Using DeclarePackage we can setup automatic loading of packages.

DeclarePackage["MyPackage`",{"ExportFun1"}]

The package "MyPackage" will be loaded as soon as the symbol ExportFun1 will be used in the current session.

??ExportFun1

How to Generate a Package File

Basically, there are 3 methods to generate a package file:

Write the .m file in a text editor.

Write the package in a notebook and save it using "File -> Save As Special … -> Package Format".

Write the package in a notebook. Mark all cells that contain code that must go into the package as initialization cell. When saving the notebook for the next time, Mathematica will ask you whether you want to generate an "auto save package". Answering "Yes" will automatically save the content of all initialization cells (i.e. the package!) in a .m file with the same basename as the notebook. Moreover, it will update the package file everytime you save the package.