Search code examples
wolfram-mathematicascoping

Using Context as a scoping construct in Mathematica


Thinking about a solution to my previous question about switching between numerical and analytical "modes" in a large Mathematica project, I thought about the idea of using Context as a scoping construct.

The basic idea is to make all numerical value assignments in their own context, e.g.

Begin["Global`Numerical`"]
par1 = 1;
par2 = 2;
...
End[]

and have all the complicated analytical functions, matrices, etc. in the Global context.

Ideally I would be able to work in the Global context and switch to everything being numeric with a simple Begin[Global'Numeric'] and switch back with End[].

Unfortunately this doen not work, since e.g. f[par1_,par2_,...] := foo defined in the Global context will not use par1, par2, etc which have been defined in a sub context of Global.

Is there a way to make sub contexts inherit definitions from their parent context? Is there some other way to use contexts to create a simple switchable scope?


Solution

  • Well, here's one way to get around (what I think) is your problem by adjusting $ContextPath appropriately:

    SetOptions[EvaluationNotebook[], CellContext -> "GlobalTestCtxt`"];
    Remove[f, GlobalTestCtxt`Numerical`f, par1, par2];
    f[par1_, par2_] := {par1, par2};
    
    savedContextPath = $ContextPath;
    Begin["GlobalTestCtxt`Numerical`"];
    Print[{$ContextPath, $Context}];
    $ContextPath = DeleteCases[$ContextPath, "GlobalTestCtxt`"];
    par1 = 1;
    par2 = 2;
    End[];
    $ContextPath = savedContextPath;
    

    Now, this will evaluate analytically:

    f[par1, par2]
    

    And this numerically:

    savedContextPath = $ContextPath;
    Begin["GlobalTestCtxt`Numerical`"];
    $ContextPath = Prepend[$ContextPath, $Context];
    f[par1, par2]
    End[];
    $ContextPath = savedContextPath;
    

    The reason I say it's fragile is that unless you are careful, it's easy to get the symbol into the wrong context. For instance, suppose you forgot to evaluate f in the global context before evaluating the "numerical" block. Well, now your numerical block will not work simply because it'll turn to a (perfectly valid) symbol GlobalTestCtxt`Numerical`f, which you have inadvertently entered into the symbol table when you first evaluated the numerical block. Because of potential bugs like this, I personally don't use this approach.

    Edit: fixed a bug (it is necessary to hide the "Global" context when doing assignments in numerical context)