Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Is it a good idea to Initialize variables globally inside a package ? will they be local by default to that package ?

share|improve this question
    
Ok, but how to you proceed if I want to define a constant which should be used in the notebook which loads the package? –  gogoolplex Jan 18 '13 at 11:44
1  
There is a lot of documentation about packages and contexts/scoping on this site. Please search for that as a starting point (see e.g. the right hand side of this page: "Related". –  Yves Klett Jan 18 '13 at 11:49
1  
I don't think it makes sense to have a local global variable. Can you clarify? (FWIW, I generally prefer a point-free style where possible.) –  Oleksandr R. Jan 18 '13 at 12:07
    
Is your use case something along the lines of a function call to your package requiring one of a number of constants you wish to define: result = mySpecialPackageFunction[x,y,z,myScaleFactor->mySpecialConstant] ? –  image_doctor Jan 18 '13 at 12:50
    
@Oleksandr to what extent is "point-free style" possible in Mathematica? If you cannot simply use Composition what do you do? How are you defining this? –  Mr.Wizard Jan 18 '13 at 14:19
show 1 more comment

2 Answers

General considerations

It often does make sense to use global variables inside your package. And sometimes, you simply can't avoid that. By global here I mean variables which are defined outside any function. There are two distinct cases here:

  • A variable is defined in the Private` subsection of the package and not exported as a part of the package's public interface (then it is an "internal" global variable used by the implementation of the package).
  • A variable is exported to the user, as a part of the public interface of the package.

In all cases, global variables are useful when the package needs to store some state. There are several cases when this may be needed. I will divide them into two broad categories / sets of use cases:

  • Certain data must be recorded during the installation (loading) of the package, so that they can be used later. They may or may not need to change later, but it may be that they can only be obtained during the load-time. Some examples of such are various paths to certain resources.

  • Some parameters have to be present to store the current state of the package, and are related to the run-time behavior of the package. Such variables may, for example, store the information about which resources / functions have been installed or uninstalled, or some other status variables.

While I agree that one is better off by not using global variables unless strictly necessary, I doubt that one can do without global variables in packages for all tasks. The situation is similar to static (class) variables in Java or C++: it is best to keep those to a minumum, but it is hardly possible or even desirable to completely eliminate them in all cases. The problem is so common that it has a special design pattern addressing it: the sigleton pattern, which serves to keep a single instance of a class in the system, and is, in essence, a collection of global variables (class fields).

An illustration

To not make up any artificial example, here is a small excerpt from the RLink` package (RLink.m), which I picked simply because it is a project, the code of which I am closely familiar with. By no means do I suggest this as a perfect canonical example of Mathematica code - I rather think that some of the design choices made there regarding global variables are rather typical.

BeginPackage["RLink`"]

RVector::usage = 
"RVector[type,data,attributes] represents an internal form of R vector in RLink.";
...
$RDataTypePath::usage = "$RDataTypePath is a global variable which stores a list of
 locations (directories) where RLink looks for definitions of extended data types";
...
Begin["`Private`"]
...
$testMode = ValueQ[$testConfiguration];

$projectLocation = 
        With[{pos = 
           Position[FileNameSplit[$InputFileName], "RLink"][[If[$testMode, -2,-1],1]]
          },
	  FileNameTake[$InputFileName,pos]
    ];

$RCurrentlyInstalled = False;

$RWasInstalledDuringMathematicaSession = False;

$temporaryDirectory = ApplicationDataUserDirectory["RLink"];
    ...
    $defaultDataTypeDirectory = 
    If[$testMode,
           FileNameJoin[{$projectLocation,"RLink","Kernel","DataTypes"}],
           (* else*)
           FileNameJoin[{$projectLocation,"Kernel","DataTypes"}]
    ];

$RDataTypePath = {$defaultDataTypeDirectory};
...
End[]

EndPackage[]

You can see here a bunch of global variables. The variables $testConfiguration, $testMode, $projectLocation, $temporaryDirectory and $defaultDataTypeDirectory belong to the first category of variables - they are defined at the package's load-time and store some data such as various paths, or the regime in which a package functions (such as test/debug or production), and they don't change after the peckage has been loaded. Note that some data, such as the project's absolute path stored in $projectLocation variable, can not be obtained after the package has been loaded, and so must be recorded at load-time if such information is needed (one can inject them into functions directly by wrapping the relevant portions of the package's code into a giant With, but I usually find this to be an overkill).

The variables $RCurrentlyInstalled, $RWasInstalledDuringMathematicaSession and $RDataTypePath belong to the second category in my classification. The first two allow various package functions to determine the status of current R session (and so, for example, not attempt to install R, if it has been already installed, and not attempt to send some data to R etc., if it has not yet been installed). They can change at run-time many times. The $RDataTypePath, OTOH, while also being a "run-time" global variable, has a different purpose. It is exposed to the user, and allows the user to have a better control over the behavior of the package, in this case regarding the loading of external data type definitions (it is analogous to the global $Path variable).

Summary

Global variables, either exposed to the user or kept private to the package, can be useful, and sometimes even necessary.

A variable will be public, if it is exposed to the user, for which to happen, the standard way is to give it a usage message at the public section of the package (before Begin["`Private`"]). A variable defined after Begin["`Private`"] will be private to the implementation of the package. The situation here is the same as with functions, which is no accident since the line between variables and functions is blurred in Mathematica.

Whether or not global variables are useful and / or necessary, depends on the problem being solved by the package in question. If the package defines e.g. routines for some data transformations, then it might be that no global variables are needed. However, if the package, for example, connects to some other systems and / or otherwise communicates with something having a state, then it is rather likely that some global variables may be needed.

Global variables may also be a good way to expose some package's controls to the user. They are useful in this capacity when they store quantities on which many of the package functions depend, so that it is not possible to make them local and / or optional parameters to some particular function.

share|improve this answer
    
Very nice explanation. Your absolutely right. I did not consider the usage of global variables in this way. –  g3kk0 Jan 18 '13 at 16:23
    
@AlexanderSchmitz Thanks. This answer came out of my continuous attempts to eliminate global variables in my code, since a while ago I held an opinion on this very close to what you formulated in your answer. I finally came to a conclusion that this is not often possible or even desirable - which is what I tried to express here. –  Leonid Shifrin Jan 18 '13 at 16:25
    
As a matter of good practice, every package with the potential for use by anybody except the author ought to export three global variables (within its context): one specifying the package version, a second specifying the package date, and a third giving the name of the package author. It is typically much easier to check whether one has the current version, etc., by evaluating such globals instead of trying to locate and then opening the .m file itself. –  murray Jan 18 '13 at 21:51
    
@murray I partly agree, but versioning is a more complex matter. I am in fact working on a versioning system / package manager for Mathematica projects that will be robust. Generally, proper versioning should IMO be a task for a computer, not a user, since maintaining the version variable can be pretty error-prone. In other words, as for versioning, I think it is best factored out from the rest of the package's code. My personal opinion, of course. –  Leonid Shifrin Jan 18 '13 at 22:55
    
@Leonid Shifrin: All I'm asking is that when an author releases a package to others, s/he include globals (within the package context) giving the version and author. If this be done automatically via a versioning system, fine; but by-hand takes just a moment. I use at least 2 for-pay add-ons that neglect this simple step. –  murray Jan 19 '13 at 14:54
show 4 more comments

I think it is not a good idea to use global variables inside a package at all. Things you put into a package are thought to be local inside the package. However, what you can do is to make for example a certain function available for other packages that have your package loaded into their context.

This is done in the following way: You first define your package and its context which is called test in this case. If you want to make a function available for other packages, you can define a usage for that function in the exported symbols region.

(* Mathematica Package *)

BeginPackage["test`"]
(* Exported symbols added here with SymbolName::usage *)  

fun::usage="
fun[x_]
adds one to x.
";
Begin["`Private`"] (* Begin Private Context *) 

fun[x_]:= x+1

End[] (* End Private Context *)

EndPackage[]

Now the function fun is accessible from other packages that have the package loaded into their context. What you could do is to define a variable inside that area. This variable will then be global in the context test, but i would not recommend doing that.

I recommend reading about the usage of packages in the help of Mathematica, as others already suggested.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.