Setting Up Wolfram Language Packages

In a typical Wolfram Language package, there are generally two kinds of new symbols that are introduced. The first kind are ones that you want to “export” for use outside the package. The second kind are ones that you want to use only internally within the package. You can distinguish these two kinds of symbols by putting them in different contexts.

The usual convention is to put symbols intended for export in a context with a name Package` that corresponds to the name of the package. Whenever the package is read in, it adds this context to the context search path, so that the symbols in this context can be referred to by their short names.

Symbols that are not intended for export, but are instead intended only for internal use within the package, are conventionally put into a context with the name Package`Private`. This context is not added to the context search path. As a result, the symbols in this context cannot be accessed except by giving their full names.

Contexts conventionally used in Wolfram Language packages.

There is a standard sequence of Wolfram Language commands that is typically used to set up the contexts in a package. These commands set the values of (RefLink[$Context, paclet:ref/$Context]) and (RefLink[$ContextPath, paclet:ref/$ContextPath]) so that the new symbols which are introduced are created in the appropriate contexts.

The standard sequence of context control commands in a package.

BeginPackage["Collatz`"]

Collatz::usage =
        "Collatz[n] gives a list of the iterates in the 3n+1 problem,
        starting from n. The conjecture is that this sequence always
        terminates."

Begin["`Private`"]

Collatz[1] := {1}

Collatz[n_Integer]  := Prepend[Collatz[3 n + 1], n] /; OddQ[n] && n > 0

Collatz[n_Integer] := Prepend[Collatz[n/2], n] /; EvenQ[n] && n > 0

End[ ]

EndPackage[ ]

The sample package Collatz.m.

Defining usage messages at the beginning of a package is the standard way of making sure that symbols you want to export are created in the appropriate context. The way this works is that in defining these messages, the only symbols you mention are exactly the ones you want to export. These symbols are then created in the context Package`, which is then current.

In the actual definitions of the functions in a package, there are typically many new symbols, introduced as parameters, temporary variables, and so on. The convention is to put all these symbols in the context Package`Private`, which is not put on the context search path when the package is read in.

This reads in the sample package given above:

<<ExampleData/Collatz.m

The (RefLink[EndPackage, paclet:ref/EndPackage]) command in the package adds the context associated with the package to the context search path:

$ContextPath

The Collatz function was created in the context Collatz`:

Context[Collatz]

The parameter n is put in the private context Collatz`Private`:

?Collatz`Private`*

In the Collatz package, the functions that are defined depend only on built-in Wolfram Language functions. Often, however, the functions defined in one package may depend on functions defined in another package.

Two things are needed to make this work. First, the other package must be read in, so that the functions needed are defined. And second, the context search path must include the context that these functions are in.

You can explicitly tell the Wolfram Language to read in a package at any point using the command <<context`. ("Files for Packages" discusses the tricky issue of translation from system-independent context names to system-dependent file names.) Often, however, you want to set it up so that a particular package is read in only if it is needed. The command (RefLink[Needs, paclet:ref/Needs])["context`"] tells the Wolfram Language to read in a package if the context associated with that package is not already in the list (RefLink[$Packages, paclet:ref/$Packages]).

Functions for specifying interdependence of packages.

If you use (RefLink[BeginPackage, paclet:ref/BeginPackage])["Package`"] with a single argument, the Wolfram Language puts on the context search path only the Package` context and the contexts for built-in Wolfram Language symbols. If the definitions you give in your package involve functions from other packages, you must make sure that the contexts for these packages are also included in your context search path. You can do this by giving a list of the additional contexts as a second argument to (RefLink[BeginPackage, paclet:ref/BeginPackage]). (RefLink[BeginPackage, paclet:ref/BeginPackage]) automatically calls (RefLink[Needs, paclet:ref/Needs]) on these contexts, reading in the corresponding packages if necessary, and then making sure that the contexts are on the context search path.

Context manipulation functions.

Executing a function like (RefLink[Begin, paclet:ref/Begin]) which manipulates contexts changes the way that the Wolfram Language interprets names you type in. However, you should realize that the change is effective only in subsequent expressions that you type in. The point is that the Wolfram Language always reads in a complete input expression, and interprets the names in it, before it executes any part of the expression. As a result, by the time (RefLink[Begin, paclet:ref/Begin]) is executed in a particular expression, the names in the expression have already been interpreted, and it is too late for (RefLink[Begin, paclet:ref/Begin]) to have an effect.

The fact that context manipulation functions do not have an effect until the next complete expression is read in means that you must be sure to give those functions as separate expressions, typically on separate lines, when you write Wolfram Language packages.

The name x is interpreted before this expression is executed, so the (RefLink[Begin, paclet:ref/Begin]) has no effect:

Begin["a`"];Print[Context[x]];End[]

Context manipulation functions are used primarily as part of packages intended to be read into the Wolfram Language. Sometimes, however, you may find it convenient to use such functions interactively.

This can happen, for example, if you go into a dialog, say using (RefLink[TraceDialog, paclet:ref/TraceDialog]), while executing a function defined in a package. The parameters and temporary variables in the function are typically in a private context associated with the package. Since this context is not on your context search path, the Wolfram Language will print out the full names of the symbols, and will require you to type in these full names in order to refer to the symbols. You can however use (RefLink[Begin, paclet:ref/Begin])["Package`Private`"] to make the private context of the package your current context. This will make the Wolfram Language print out short names for the symbols, and allow you to refer to the symbols by their short names.