Mathematica Technical Note:
Information about Contexts and Packages

What This Note Is About

This technical note expands on the information about contexts and packages in The Mathematica Book.  It explains some subtle points about packages, functions, and variable names.  You can do a lot of work with Mathematica without ever needing to read this document.  (In fact, you will probably need to acquire at least a little experience with Mathematica before some of the matters discussed here will make sense to you.) If you use Mathematica primarily as a super-calculator, define only simple functions that you use only once, and use only those packages that were included in the Mathematica distribution kit, then you will not need the information here.

But sooner or later you will probably want to define a sophisticated function, or group of related functions, that performs some standard task and that you will want to use over and over again.  You will want to save the specification of such a ``package'' of functions to a file so that you can re-load it without typing it all in again, and you may want to build other packages that extend, enhance, or rely upon the functions in this one. When you are ready to do these things, you should read this article.

The purpose of this technical note is to provide a gradual introduction to the techniques of manipulating contexts and writing packages, with examples to show how the commands work and how they should be used.  All of the information presented here is available in The Mathematica Book in a terse form more suitable for reference.  Section 2.6.13 discusses reading files with Get, and Section 2.7 treats names, contexts, and packages.

If you plan to do a great deal of package development in Mathematica, you will also find Roman Maeder's book Programming in Mathematica, published by Addison-Wesley, a valuable resource.  This book is a guide to good programming style in Mathematica, written by another of the co-authors of Mathematica.  It discusses, in detail and with examples, when and how to use advanced features of Mathematica's programming language.  The discussion of contexts and packages in Maeder's book includes some subtleties not discussed in this note.

Name Collisions Are Hazardous

To understand why you need to be careful in writing packages, you should be aware of some pitfalls in the use of variables inside function definitions.  Try this example.

Define this function, which makes a list of the first five powers of its argument.

In[1]:= powers[x_]:= Table[x^i,{i,5}]

It works as expected for a number.

In[2]:= powers[2]
Out[2]= {2, 4, 8, 16, 32}

It also works for a variable other than i.

In[3]:= powers[t]
2   3   4   5
Out[3]= {t, t , t , t , t }

It does something mysterious for i, however.
We do not get the first five powers of i.

In[4]:= powers[i]
Out[4]= {1, 4, 27, 256, 3125}

This example illustrates a subtle but important point, and is worth understanding carefully.  The expression powers[t] got re-written, according to the rule for powers, into the expression Table[t^i,{i,5}], which did what we wanted it to. But by the same rule, the expression powers[i] got re-written into Table[i^i,{i,5}], which produced the result we got but did not want.

The problem occurred because there was a ``collision'' between the two ways that the variable i was used: as an argument to powers, and as an index variable for the Table operation in the definition of powers.  This kind of collision between different uses of variable names is dangerous, because it can lead to undesirable and unexpected results.

The only way to avoid this kind of name collision is to make sure that you never give as arguments to a function any of the symbols used in the definition of that function.  In the case of simple functions that are defined in a single Mathematica session and intended to be used only for a few moments, it is easy to remember what variables you've used and to avoid re-using them.  But suppose you are writing a ``package'' of general-purpose functions to be used over and over again.  You expect still to be using the package months after you have written it, but you know you will never remember all the names in the package for that long.  And you shouldn't have to.

Use Contexts to Avoid Name Collisions

Mathematica's mechanism for avoiding name collisions is the  context.  A context is a collection of Mathematica variables.  A context has a name, which is formed according to the same rules as variable names except that it must end with the special character ` (the grave accent, character number 96 in the ASCII alphabet).  A context may be embedded inside another context; for example, MyContext`Subcontext` would represent a sub-context named Subcontext` of the context MyContext`.

Every variable that is used in a Mathematica session is placed into a context when it is first mentioned.  By default, variables are placed into the context named Global`, but you can use the commands Begin and BeginPackage to tell Mathematica henceforth to place variables into other contexts.  You can also prepend a context name to a variable name to force Mathematica to place the variable into that context: MyContext`x refers to a variable named x that resides in a context named MyContext`, regardless of what default context Mathematica is currently using.

The important fact about contexts is this: you can have two different variables that have the same name, provided that they reside in different contexts.  A variable named i that resides in the context MyContext` is, to Mathematica, a completely different object from a variable named i that resides in the default context Global`.

This means that we can use contexts to avoid name collisions.  Try the following example.

We try again the example from the previous section, this time with
the index variable i placed in the context MyContext`.

In[5]:= newpowers[x_] :=

It still works as expected for a number.

In[6]:= newpowers[2]
Out[6]= {2, 4, 8, 16, 32}

It still works for a variable other than i.

In[7]:= newpowers[t]
2   3   4   5
Out[7]= {t, t , t , t , t }

But now it also works for i.

In[8]:= newpowers[i]
2   3   4   5
Out[8]= {i, i , i , i , i }

The function newpowers works as we want it to even when we give it i as an argument, because (if you have not used Begin or BeginPackage) the variable i without any context prefix means Global`i, and Global`i is a different variable than the variable MyContext`i that we used in the definition of newpowers.

Of course, if we tried newpowers[MyContext`i] we would get the old, undesirable result, but that will happen only if someone who knows what variable and context we used in the definition of newpowers specifically sets out to thwart our intentions.  In general, anyone who knows enough about your code can break it if he wants to, but such troublemakers deserve what they get.

The usefulness of contexts in writing packages is now clear.  When we begin the definition of a package, we set aside a private context for that package.  We use BeginPackage and Begin to establish this context as the default, so that we avoid the tedium of typing context prefixes like MyContext` all over the place.  We place into this context all the variables that we use in the definitions of the package's functions, so that they will be safely out of the way of name collisions with the variables of unwary users.  When we are finished with the definitions of the functions in the package, we use End and EndPackage to restore Global` as the default context, and our functions are ready for use and safe from name collision.

How Names Are Sought and Created in Contexts

To really understand how to use contexts, you need to know how Mathematica decides where to put a variable, and how Mathematica finds the variable again after it has put it there.

When you type an expression into Mathematica, Mathematica scans the expression to determine whether it is syntactically correct.  Mathematica must do this before it can evaluate the expression.  One purpose of the scanning process is to identify the names that appear in the expression and figure out which variables they represent. Mathematica's rule for doing this is as follows: when the scanner encounters a name, it checks to see whether a variable with that name already exists.  If so, the name represents the first variable the scanner finds that has that name.  If no variable with that name is found, a new variable with that name is created.

There are two unanswered questions here: since there can be many variables of the same name residing in different contexts, how do we know which of these variables the scanner will find? And where does Mathematica put the new variable that it creates if none is found?

Mathematica has two special variables whose values control how the search for old variables and the creation of new ones are performed.  They are $Context and $ContextPath.  The value of $Context must be a context name, and the value of $ContextPath must be a list of context names.  When Mathematica first starts up, $Context is "Global`" and $ContextPath is {"Global`", "System`"}. (System` is the context in which all of Mathematica's primitive functions, such as Table, Integrate, Factor, and Plot, reside.)

When Mathematica's scanner is looking for a variable with a given name, it looks first in the context named by $Context, and then in the contexts named in $ContextPath in the order of their appearance.  It stops as soon as it finds a variable of the given name. If none of the contexts mentioned by $Context and $ContextPath contains a variable of that name, the scanner creates one in the context named by $Context.

Of course, the rule for deciding what context a variable belongs in is not needed if the variable carries an explicit context prefix.  If the scanner sees such a construction, it simply places the variable in the indicated context.  As we saw before, MyContext`x always refers to a variable named x that resides in a context named MyContext`, regardless of the values of $Context and $ContextPath.

The function Context can be used at any time to tell what the scanner will do in the present situation.  Context[symbol] returns the name of the context in which symbol resides.  Of course, if the symbol does not already exist, it is created in the context $Context as a result of Mathematica's having scanned the statement.  By contrast, the command Context["string"] searches for a symbol whose name is string, but if none is found, it prints a warning message and does not create a new symbol.

A very subtle but important point is that Mathematica scans an entire input expression before evaluating any of it.  This can lead to unexpected behavior, as in the following example.

We initiate a new context and create a new variable.

In[9]:= Begin["MyContext`"]; Context[newvariable]
Out[9]= Global`

We create another new variable.

In[10]:= Context[anothernewname]
Out[10]= MyContext

We restore the old context structure.

In[11]:= End[];

What happened in the first line?  Why did the symbol newvariable get created in the context "Global`"? The entire compound expression consisting of the Begin and Context commands was scanned before any of it was evaluated.  This means that the symbol newvariable was encountered before the command Begin["MyContext`"] reset the value of $Context.  Since newvariable was a new name, it had to be created, and since $Context had not been changed yet, newvariable was created in the context Global`.  When the evaluation happened, the Begin command did indeed change the value of $Context before the Context command was executed, but the symbol newvariable had already been established to reside in the context Global`, as the Context command bears witness.

In the second line, $Context had already been reset before the input was scanned, so anothernewname was created in the context MyContext` as expected.

This explains why Mathematica is sometimes unable to evaluate a function even after you have loaded the package that defines it.  Suppose you want to draw a picture of an icosahedron, and so you type Show[Graphics3D[Icosahedron[]]], forgetting for a moment that the function Icosahedron is defined in the package Polyhedra.m.  If you have not yet loaded this package, Mathematica will tell you that Icosahedron is an ``unknown graphics object'', and display nothing.  Realizing what has happened, you give the command to load Polyhedra.m and then re-issue the Show command.  Alas! you receive the same error message as before.  What has happened?

The first time you gave the Show command, Mathematica had never seen the symbol Icosahedron before, so Mathematica created it in the context named by $Context, which (unless you had changed it) was Global`.  When you loaded Polyhedra.m, the package created a symbol of its own named Icosahedron, but stored this symbol in the context Polyhedra`.  All of the package's information about how to draw icosahedra got attached to the symbol Polyhedra`Icosahedron.  The last thing the package did when it was loaded was to restore $Context to the value Global`.  So when you executed the second Show command the symbol Global`Icosahedron that was created by the first Show command was found, because the context named by $Context is always the first context that Mathematica searches. Global`Icosahedron is a different symbol than Polyhedra`Icosahedron, and no information about icosahedra or anything else was associated with it, so the second Show command also failed.

When one variable causes another variable of the same name not to be seen, we say that the first variable shadows the second.  Fortunately, there is an easy solution for this kind of problem.  The function Remove can be used to remove a symbol entirely.  In the present example, if you give the command Remove[Icosahedron] then the symbol Global`Icosahedron is removed, so that it no longer shadows the symbol Polyhedra`Icosahedron, and the command Show[Graphics3D[Icosahedron[]]] now draws the desired picture.

There are ways of manipulating contexts so as to guarantee that the symbols in the packages you write will never be shadowed by global variables.  These techniques are subtle, and are discussed (with examples) in the book Programming in Mathematica mentioned in the introduction.  Since it would take more than a short article to discuss all the possible variations, we will simply refer you to that book if you feel you need all the details.

The example above also explains a puzzling phenomenon that you may encounter if you use a version of Mathematica that supports a Notebook Front End (Mathematica for the Macintosh or NeXT computers, for example).  In these versions of Mathematica it usually will not work to open and evaluate as a Notebook a package that was not created and stored as a Notebook.  (At this time, most versions of Mathematica for the UNIX operating system do not include Notebook Front Ends, and the packages created for those versions are not structured as Notebooks.) If you try to open such a package as if it were a Notebook, then (assuming you have not used the Defaults Notebook to reset cell style defaults) Mathematica opens the package as a single huge active initialization cell and offers to evaluate the cell.  But since the entire cell is scanned before it is evaluated, BeginPackage and Begin commands in the package do not have a chance to act until after all the names defined by the package have been created--in the wrong context.

In fact, you can sometimes use the Options settings in the Open File dialog box to open such a package as a Notebook and evaluate it successfully.  One of the options is to begin a new cell at each blank line instead of opening the file as one huge cell.  If you select this option, and if the package was created with blank lines separating the commands, then every command will be stored in its own cell, including the context manipulation commands.  Since Mathematica scans and evaluates a Notebook one cell at a time, this will allow BeginPackage and Begin commands in the package to be executed before the statements that they are intended to affect are scanned, solving the problem.  Mathematica developers who use versions of Mathematica that do not support Notebooks are encouraged to structure their packages with blank lines to make this approach possible.

Of course, the same hazard applies any time you give a command that changes $Context or $ContextPath in the same cell with another command that depends on the new values in order to have the symbols properly understood.  For example (skipping ahead a bit), it will not work to put the commands Needs["Graphics`Polyhedra`"]; and Show[Graphics3D[Icosahedron[]]]; in a single cell, because Icosahedron is scanned and placed in Global` before the Needs command is executed.  (The Needs command is discussed below.) In general, context manipulation commands belong in cells by themselves.

Finally, notice that when scanning an expression to decide whether the variables named in the expression already exist, Mathematica looks only in the contexts named in $Context and $ContextPath (unless some variable has its context specified explicitly).  This means that if a certain context is not mentioned in either of $Context and $ContextPath, then the variables in that context are invisible to the scanner.  Put another way, if we want to create some variables that can be used by our own functions but cannot be entered by the user (without explicit context specifications), then we should put the variables in a context that is not mentioned in $Context or $ContextPath.  Careful exploitation of this idea is what context manipulation in packages is all about, as we shall see in the next section.

Using Contexts within Packages

Four context manipulation commands are the key to avoiding name collisions in packages.  We discuss them here in enough detail to explain what they are used for, but you should refer to The Mathematica Book for detailed information, and to Programming in Mathematica for explanations and examples.  You would probably also find it informative to skim through some of the packages that were distributed with your copy of Mathematica to see how they are structured.

Of the four crucial commands, two establish new contexts and two restore old ones.





You could, of course, manipulate contexts directly, without using these commands, by making explicit assignments to $Context and $ContextPath, but using the Begin/End commands automates the necessary steps, and reduces the chance of error.

Begin and End should be used in pairs, as should BeginPackage and EndPackage.  Both Begin and BeginPackage modify $Context, and BeginPackage modifies $ContextPath as well, but each command saves the previous values of these variables on a stack, to be popped off and restored by the corresponding End command.

The Begin and End pair is the simpler of the two.  Executing Begin["MyContext`"] saves the current value of $Context and then makes $Context be "MyContext`".  This means that new variables will be created in the context MyContext` rather than in Global` (or whatever context was previously in effect).  Variables that are already in MyContext` will also be recognized by the scanner.  This persists until the corresponding End[] statement is executed, which restores the previous value of $Context.

The command BeginPackage["MyContext`"] first saves the values of both $Context and $ContextPath.  Then it not only assigns "MyContext`" to $Context, but also assigns {"MyContext`","System`"} to $ContextPath. The effect of this is not only to cause new variables to be created in MyContext` instead of Global`, but also to cause all names not already mentioned in MyContext` or System` to be regarded as new, even if they had previously been used to name variables in Global`.  EndPackage[] first restores the values of $Context and $ContextPath, but then prepends the context established by the BeginPackage command to $ContextPath.  This means that Mathematica goes back to creating new variables in the context it was using before (probably Global`), but the names in the new context (MyContext` in this example) will still be recognized.

Note that since End[], unlike EndPackage[], does not modify $ContextPath, the context established by a Begin command becomes ``invisible'' after the End command is executed.  Variables in this context can thereafter be referenced only with explicit context prefixes, because the context is not mentioned by either $Context or $ContextPath and so the scanner will never place variables into it.

An example is now in order.  Here is our previous example, converted into a package, and with Print and Context statements sprinkled through it to show us what's going on.

These are the default values.

In[12]:= Print[$Context]; Print[$ContextPath];
{Global`, System`}

In[13]:= BeginPackage["NewContext`"];

BeginPackage resets both variables.

In[14]:= Print[$Context]; Print[$ContextPath];
{NewContext`, System`}

This not only documents the function, but causes the new variable fivepowers to be created in the context NewContext`.

In[15]:= fivepowers::usage = "fivepowers[x] returns a list of the first five powers of x.";

In[16]:= Begin["`Private`"];

Now we are in a subcontext.

In[17]:= Print[$Context]; Print[$ContextPath];

{NewContext`, System`}

In[18]:= fivepowers[x_] := Table[x^i,{i,5}]

The variable i is created in this private subcontext, even though it already exists in Global`, because Global` is not presently in $Context or $ContextPath.

In[19]:= Context[i]
Out[19]= NewContext`Private`

Since fivepowers was mentioned before the private context was initiated, it resides in NewContext`.

In[20]:= Context[fivepowers]
Out[20]= NewContext`

This undoes the effect of the Begin, making the private context (and the variable i it contains) invisible.

In[21]:= End[];

In[22]:= Print[$Context]; Print[$ContextPath];
{NewContext`, System`}

This undoes the effect of BeginPackage.

In[23]:= EndPackage[];

Note, however, that NewContext` has been added to $ContextPath.

In[24]:= Print[$Context]; Print[$ContextPath];

{NewContext`, Global`, System`}

This symbol is still visible, because NewContext` is on $ContextPath.

In[25]:= Context[fivepowers]
Out[25]= NewContext'

But the i we see now is the one in Global` and not the private one.

In[26]:= Context[i]
Out[26]= Global`

The general scheme in defining a package is as follows:

Use BeginPackage to establish a new context to store the variables for the
package, and also to hide previously created variables in other contexts so that they
will not be seen by the scanner and used in the new package's functions.
Give usage messages for all the symbols you wish to be publicly available.  This
establishes the symbols in the public context created by the BeginPackage
command, so that they will still be visible after the EndPackage command is
Use Begin to establish a private context to store the variables used by the
package that you do not want publicly accessible.
Define the functions in the package.  Any symbols not mentioned between the
BeginPackage and Begin commands (other than System` symbols) will be
stored in the private context.
Use End[] to close off the private context and make it invisible.
Use EndPackage[] to close off the package's public context (leaving it
visible), and to restore to visibility Global` and any other contexts that were on
$ContextPath before the BeginPackage command was executed.

There is only one other point that needs mentioning here.  BeginPackage leaves the context System` on $ContextPath for an obvious reason: without it, you could not refer to any of Mathematica's primitive commands to build your package's functions (except by using explicit context prefixes).  But suppose you need to refer to symbols that are in other contexts than System`, such as functions defined by other packages? Suppose for example that you are writing a package of extended graphics routines, and you want to define your new functions in terms of others that are defined by the package Polyhedra.m and stored in the context Polyhedra`.  You need not type the context prefix on each symbol defined by the polyhedra package.  The BeginPackage command accepts optional extra arguments, which are the names of contexts to be retained in the $ContextPath, allowing the symbols in those contexts to remain visible.  In fact, if the contexts named do not already exist, the packages that define them are loaded automatically, by a process explained in the next section.

Naming and Loading Packages

Because of the important role of contexts in defining packages of functions, it is conventional to make the name of the package the same as the name of the context it defines.  For example, the package Polyhedra.m, which you will find in the Graphics subdirectory in the Packages directory in the main Mathematica directory, defines a context named Graphics`Polyhedra`.

Mathematica has two commands for loading packages: Get["filename"], which may be abbreviated as <<"filename" (but see the cautionary statements below), and Needs["contextname"].  Both commands are used to cause Mathematica to read a file and execute the Mathematica commands in a file, but there are important differences.

Getting What You Want with Get

Get is intended to be used to unconditionally load and execute any collection of commands stored in a file (whether or not the commands make up the definition of a package, establishing private contexts and so forth).  The argument of Get is the name of the file to be loaded.  The rules for what constitutes a legal file name are of course determined by the operating system and not by Mathematica.  This is especially important to remember when you are using a Front End connected to a remote Kernel, because  Get always reads files from the file system of the computer that is running the Kernel.

Suppose, for example, that you wanted to use  Get to load the package Polyhedra.m we mentioned earlier.  If you were running a local Kernel on a Macintosh, you would give the command Get[":Graphics:Polyhedra.m"], and Mathematica would read the file from a disk on your Macintosh.  But if you were running a remote Kernel on a computer that used UNIX, you would say Get["Graphics/Polyhedra.m"], and the copy of Polyhedra.m that was stored on the remote computer's file system would be read.  If you were using the MS-DOS version of Mathematica on a 386-based MS-DOS computer, the file naming conventions of MS-DOS would require you to say Get["Graphics\\Polyhedra.m"] (the backslash character must be repeated because Mathematica treats a backslash inside a string as an ``escape character'').

Note that these examples use ``relative'' file names.  MS-DOS, UNIX, and the Macintosh operating system all have essentially similar notions of ``relative'' and ``absolute'' file names.  An absolute file name (such as MyDisk/MyDirectory/MySubDirectory/MyFile) specifies a file on a disk unambiguously in terms of its position in the directory structure of that disk.  A relative file name gives the position of the file, not in terms of the outermost directory on the disk, but in terms of its position relative to the ``current'' directory.

Mathematica allows you in effect to maintain many current directories at once, by making entries in the variable $Path.  Just as $ContextPath is a list of contexts to be searched when Mathematica is trying to find a variable, $Path is a list of directories (or folders) to be searched when Mathematica is trying to find a file.  When you ask Mathematica to open a file, and you specify the file with a relative file name, Mathematica first tries to find the file relative to the first directory listed in $Path.  If the file isn't there, Mathematica re-interprets the name relative to the second entry in $Path, and so on until it either finds the file or runs out of directories to search.

By contrast, when you issue a  Get command with an absolute path name, Mathematica has only one possible file to consider; Mathematica either finds the file, in which case it loads it, or else it does not find the file, in which case it prints an error message.

Mathematica provides for the command Get["string"] the special abbreviation <<"string", but there is an important difference.  The << notation always treats its argument as a string, even if no string quotes are present.  Suppose you have a file named MyFile, and you perform the assignment filevar="MyFile".  Then executing Get[filevar] will cause Mathematica to attempt to load MyFile, but executing <<filevar will cause Mathematica to try to load a file named filevar.  Also, when Mathematica's scanner is parsing a command of the form <<string (where the string is given without quotation marks), it stops when it encounters a character that would not be legal in a variable name, even if the character would be legal in a file name.  For example, the commands Get["My File"] and <<"My File" both attempt to load a file named My File, but the command <<My File will be parsed as a multiplication, with the result of loading the file named My multiplied by the symbol File!

Meeting Your Needs with Needs

Because the  Get command uses operating system file names, whose syntax varies from system to system, you have to give its arguments in different forms to achieve the same effects on different systems.  This is undesirable if your goal is to develop packages that work the same way under different versions of Mathematica running on different machines.  For example, suppose you write a collection of graphics utilities that builds on and extends the functions that are defined in the standard Polyhedra.m package.  You want to be able to assume for the purposes of your definitions that the functions defined in Polyhedra.m have already been loaded, and you don't want the user of your package to have to remember this assumption.  You could put a  Get command to load Polyhedra.m at the beginning of your package, but no matter which version of the command you give, it will be wrong for some operating systems.  What should you do?

There is another subtle point: you probably do not want your package to re-load Polyhedra.m if the user has already loaded it manually.  This would waste time, and would cause any initialization that might be performed by Polyhedra.m to be re-executed.  In the case of Polyhedra.m this would probably be harmless, but not all packages are designed to be re-initialized, so in general it might cause problems.

Mathematica provides Needs to address these problems.  You can use Needs to declare that your package ``needs'' another one.  The argument to Needs is the name of a context, not a file.  Executing Needs["contextname"] within a package tells Mathematica to check whether the given context is available, and if it isn't, to load a package that defines it.  If the context requested is the one named by $Context, or is on $ContextPath, then Needs does nothing, so there is no risk of re-initialization. Since context names, unlike filenames, are Mathematica artifacts, the argument to Needs is independent of the operating system that happens to be running the Mathematica Kernel.

It is perfectly possible to have a context that Needs considers ``unavailable'' because it is not named by either $Context or $ContextPath.  Private subcontexts created by Begin inside a package fall into this category, for example.  Suppose that SecretContext` is such a context; then the command Needs["SecretContext`"] will attempt to load a file that defines SecretContext` even though that context already exists.  This is an unusual and probably unintentional occurrence.  In general you should try to use context names that are distinctive and descriptive enough that you will not accidentally use the same name for both a private and a public context (or for two public contexts either, for that matter). A good rule is to make each ``secret'' context a subcontext of the public context defined by the package that uses it, and to reserve top-level names for public contexts only.

Of course, in order to load a file that defines a given context, Needs has to have some way of manufacturing file names from context names.  Needs invokes another Mathematica function, ContextToFilename, to perform this task.  Since file names are system-dependent, the results of ContextToFilename will vary from one system to another.  ContextToFilename relies on the aforementioned convention relating the names of packages and their contexts to make an ``intelligent guess'' about the appropriate file name, and it formats the file name in accordance with the requirements of the operating system.

The file names manufactured by ContextToFilename are usually suitable; for example, if you are using a local Kernel on a Macintosh computer ContextToFilename["xxx`"] will produce the name xxx.m and ContextToFilename["xxx`yyy`"] will produce the name :xxx:yyy.m.  (A Kernel running on a UNIX computer would produce  xxx.m and xxx/yyy.m, respectively, from these commands.)  But you can modify ContextToFilename by adding your own rules to handle packages whose file names ContextToFilename mangles (you will probably have to Unprotect[ContextToFilename] in order to do this).  Say you have a file named My File that defines a context MyContext`.  You could make the definition

ContextToFilename["MyContext`"] := "My File"

and then executing Needs["MyContext`"] would read your file, rather than attempt to load MyContext.m.  In fact, in this case you would have to do it this way.  The default method of converting context names to file names is inappropriate for three reasons:

the file name does not end in .m,
the file name is not the same as the name of the context the package defines, and
the file name contains a space, which is legal in a Macintosh file name but not
legal in a Mathematica context name.

Once it has determined that a file needs to be loaded, and obtained its name from ContextToFilename, Needs implicitly calls Get to do the actual loading.  This means that all of the rules about Get's use of $Path and relative versus absolute file names apply to Needs as well.

As explained above (and in The Mathematica Book), the BeginPackage command accepts optional additional arguments specifying contexts to be retained on the $ContextPath.  If a context specified in this way is not already on $ContextPath, then BeginPackage implicitly calls Needs to define the context, so ContextToFilename is used in this situation as well.

An important point is that if you specify a context as an additional argument to BeginPackage, then it is loaded before the BeginPackage command resets $ContextPath, and will remain on $ContextPath (and hence visible) after the matching EndPackage statement is executed.  By contrast, if you use a Needs command between a BeginPackage/EndPackage pair, then the specified context is available only there, and will be removed from $ContextPath by the EndPackage command when it restores the previous $ContextPath.  This method of loading a package that is accessible only by the package that loads it is called hidden import. Whether you want an imported package to be accessible outside the context it was imported for depends on the situation, and again we refer you to Programming in Mathematica for discussion and examples.

The first edition of the Mathematica book, which was written when version 1.0 of Mathematica was current, does not discuss ContextToFilename.  Instead it mentions a two-argument form of Needs, with the first argument the context name and the second the name of the file to load in order to define that context.  Although this still works in version 1.2 of Mathematica, we recommend that you use ContextToFilename instead.  A file name specified in a Needs command overrides a file name supplied by ContextToFilename, even one specified by a user-written rule.

It is important to remember that Needs is a file-loading command, not a context-defining command.  Executing a Needs command does not in itself do anything to the values of $Context and $ContextPath; it merely checks whether a context is already present and attempts to load a file if it is not.  Suppose for example the context MyContext` does not already exist, and you issue the command Needs["MyContext`"].  If the file loaded by this command does not contain a BeginPackage command that creates the context MyContext`, then re-executing Needs["MyContext`"] will re-load the file.