7
$\begingroup$

To define some package-wide functions/variables, what approach is recommended? I tried to use contexts with the Begin function, but this failed:

Clear[fun];
fun[] := Module[{},
   Begin["myContext`"];
   x = 1;
   End[];
   ];
fun[]
myContext`x

I would expect myContext'x to be 1, but it seems to be undefined. Why does this fragment fail, what should I do instead?

$\endgroup$
1
  • 1
    $\begingroup$ I gave a solution for a similar problem here, where I also discussed some of the subtleties. Note that empty Module (without localizing variables) is misleading here, and can be as well removed. $\endgroup$ Commented Sep 10, 2012 at 10:49

3 Answers 3

8
$\begingroup$

I'm just going to go ahead an rephrase the answer. The problem as Leonid points out is akin to shadowing. Here is a very simple example of the behavoir:

Remove[test`x, x]
(
Begin["test`"];
x = 42;
End[];
{x, test`x} 
)

{42,test`x}

Remove[test`x, x]
Begin["test`"];
x = 42;
End[];
{x, test`x}

{x,42}

If you use Trace You will see that in the second example everything is evaluated line-by-line, which means that when we get to x=42 the active context is test`, however in the first case, we initially evaluate the CompoundExpression which means we put Global`x into scope, which means we already have an x defined when we evaluate x=42 thus it's interpreted as Global`x=42.

A way to get around this is to use the fact that MakeBoxes will automatically remove any context currently in $ContextPath while ToExpression Automatically puts any symbol not found in any context into $Context. Here I exclude the System` context in order to avoid scoping for example Sin to test`Sin:

SetAttributes[ContextScope, HoldAll]
ContextScope[context_, expression_, exclude_: {"System`"}] :=
Block[{held},
 Block[{$ContextPath = Complement[$ContextPath, {"System`"}]}, held = MakeBoxes[expression]];
 Block[{$Context = context, $ContextPath = {context}}, 
 ToExpression[held]]
]

ContextScope["test`", x = Sin[0]]
{x, test`x}

{x,0}

This will however still put Global`x in scope, it simply does not assing a value to it. So any new call to x=somethign will assign a value to Global`x and not to test`x as would normally be expected if you had done a line by line evaluation of the context switching.

$\endgroup$
12
  • $\begingroup$ Good observation, but I would not "multiply entities". The reason for the difference is in how expressions entered in cells in the FrontEnd are parsed. The parser makes an exception for the top-level CompoundExpression (second case), parsing line-by-line - thus this works. For the first case, it parses entire body of CompoundExpression before evaluating, and therefore the symbol x is still created in the Global` context. This has been explained also here. $\endgroup$ Commented Sep 10, 2012 at 10:48
  • 1
    $\begingroup$ "I don't think you can start and stop a context within other evaluations" - sorry, but this is simply not the case, see e.g. this answer. It is actually this feature of dynamic control over the current context and other things affecting parsing ($ContextPath) which make the encapsulation mechanism of Mathematica much more flexible and powerful. $\endgroup$ Commented Sep 10, 2012 at 10:52
  • $\begingroup$ @LeonidShifrin I stand corrected. It is indeed because x is scoped upon its first appearance when the entire expression is first evaluated, which scopes it to global. Could you elaborate on "would not multiply entities" ? $\endgroup$
    – jVincent
    Commented Sep 10, 2012 at 10:55
  • $\begingroup$ Well, I just meant Occam's razor principle, in that it is always best to describe the principle from which say 10 different observations follow, than to only describe the observations alone, which would then look like magical spells. $\endgroup$ Commented Sep 10, 2012 at 10:58
  • $\begingroup$ I actually did not first notice your statement about the impossibility of dunamically changing the current context (for which the examples apparently serve as an illustration), and so initially viewed your two examples just as empirical observations of these different behaviors, thus my comment on entities multiplication :-). $\endgroup$ Commented Sep 10, 2012 at 11:02
9
$\begingroup$

The issue here is that you haven't actually defined myContext`x, just plain old x. It is possible to access global variables within a context (that's exactly what happens when you use built-in function within a package), and that is what you have done.

Clear[fun];
fun[] := Module[{}, Begin["myContext`"];
   x = 1;
   End[];];

fun[]

This is undefined.

myContext`x  
(* myContext`x *)

But you have set x:

x 
(* 1 *)

To make this work, set the context explicitly in the definition of your variable:

morefun[] := Module[{}, Begin["myContext2`"];
   myContext2`x = 1;
   End[];];

morefun[]

myContext2`x 
(* 1 *)
$\endgroup$
1
  • $\begingroup$ Thanks, if I understand correctly, Begin just sets/alters the $Context / $ContextPath variables, but if I want to use the context, I have to state this explicitly. Or use @jVincent's meta programming. $\endgroup$
    – Karsten W.
    Commented Sep 10, 2012 at 12:01
1
$\begingroup$

Just for reference, the way I go now is to define a global context in my add-on package, myTool'globals' and for every package function that uses a global (package-wide) variable, I make the package function dependent on the globals package:

BeginPackage["myTool`alterGlobalVar`", {"myTool`globals`"}];
...

I hope this will work out well for me.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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