1
$\begingroup$

Suppose you want to access a variable defined in a "surrounding" context (scoped via Begin and End) from within Manipulate. The following naive attempt does not work.

Begin["MyContext`"];
x = 123;
Manipulate[x + y, {y, 0, 1}]
End[];
x = 456; (* changes the value of Manipulate output *)

One way to fix this is by using Inactive and Activate as follows.

Begin["MyContext`"];
x = 123;
Activate@Manipulate[Inactive[x] + y, {y, 0, 1}]
End[];
x = 456; (* does not change the value of Manipulate output *)
MyContext`x = 456; (* changes the value of Manipulate output *)

Is this a robust way to achieve the desired goal or do you see any problems using this pattern in more involved examples? If so which alternative solutions do you suggest?

$\endgroup$
3
  • $\begingroup$ Not sure that I understand what you are trying to accomplish. Does Begin["MyContext`"]; Manipulate[Module[{x = 123}, x + y], {y, 0, 1}] End[]; x = 456; do what you want? $\endgroup$
    – Bob Hanlon
    Jul 1, 2021 at 22:52
  • $\begingroup$ I want to be able to refer to e.g. functions defined outside of Manipulate in a specific context like MyContext:x(colon -> backtick). $\endgroup$ Jul 1, 2021 at 22:58
  • 1
    $\begingroup$ Markdown tip: You can get a "`" in code by surrounding the code in double backticks "``": "``MyContext`x``" $\endgroup$
    – Michael E2
    Jul 2, 2021 at 22:48

3 Answers 3

3
$\begingroup$

What you are showing can in principle work, under two conditions:

  • You need to have the Begin[…] and End[…] parts in different cells. If you do not, the symbol x is already resolved to Global`x before the context is changed. This is because as a first step, the entire cell contents are converted into an expression using MakeExpression. Only then are the individual lines evaluated.

  • x must not exist in the Global` context when evaluating your code. This is because otherwise x is still resolved to Global`x, because $ContextPath still contains Global` , so no new symbol is created.¨

    Begin["MyContext`"];
    
    (* separate cell *)
    x = 123;
    
    Manipulate[x + y, {y, 0, 1}]
    
    (* separate cell *)
    End[];
    
    (* separate cell *)
    x = 456;(*does not change the value of Manipulate output*)
    

    enter image description here

Here are three approaches to make sure the second condition is not an issue:

  • Use `x instead of x. This makes sure you are referring to the x in the current context, and not Global`x

    x = 233;
    
    (* separate cell *)
    Begin["MyContext`"];
    
    `x = 123;
    
    Manipulate[`x + y, {y, 0, 1}]
    
    (* separate cell *)
    End[];
    
    (* separate cell *)
    x = 456;(*does not change the value of Manipulate output*)
    

    enter image description here

  • Change $ContextPath temporarily.

    x = 233;
    
    Begin["MyContext`"];
    oldCP = $ContextPath; $ContextPath = {"System`", $Context};
    
    (* separate cell *)
    x = 123;
    
    Manipulate[x + y, {y, 0, 1}]
    
    (* separate cell *)
    $ContextPath = oldCP;
    End[];
    
    (* separate cell *)
    x = 456;(*does not change the value of Manipulate output*)
    

    enter image description here

  • Use BeginPackage to handle $ContextPath for you.

    x = 233;
    
    (* separate cell *)
    BeginPackage["System`"];
    Begin["MyContext`"];
    
    (* separate cell *)
    x = 123;
    Manipulate[x + y, {y, 0, 1}]
    
    (* separate cell *)
    End[];
    EndPackage[];
    
    (* separate cell *)
    x = 456;(*does not change the value of Manipulate output*)
    

    enter image description here

    This makes use of the same strategy that is used for paclet development: You use BeginPackage to temporarily reset $ContextPath to a clean version, and use Begin to make a private context whose symbols are not seen from the outside. The only difference is that here, I'm using BeginPackage["System`"] in order not to create any superfluous contexts. You could also use BeginPackage["MyContext`"], and then Begin["`Private`"]. You could then expose some of the symbols by declaring them before the Begin[...] part, and hide the other ones in the MyContext`Private` subcontext.

To work around the issue mentioned by @MichaelE2 with the labels of the sliders, you can manually specify the label for the slider using the syntax {{y,0,"y"},0,1} to specify the slider.

$\endgroup$
5
  • $\begingroup$ Thank you for your explanation and nice solutions. Of both approaches I like the backtick one (first approach) best because it is concise. However, it is easy to forget the backtick and thus introduce errors. The second approach seems slightly cumbersome. Can you think of another way? If no other solution comes up, I will accept your answer. $\endgroup$ Jul 2, 2021 at 23:35
  • 1
    $\begingroup$ +1. The second method is somewhat "fragile": If you evaluate the Manipulate output, it displays MyContext`y next to the Manipulator instead of just y. $\endgroup$
    – Michael E2
    Jul 3, 2021 at 1:15
  • $\begingroup$ @MichaelE2 Thanks for pointing that out! I have added a comment at the bottom for how to work around that by specifying the label explicitly. $\endgroup$
    – Lukas Lang
    Jul 3, 2021 at 13:20
  • 1
    $\begingroup$ @MathGaudium Please see the third approach for a "cleaner" version of the second one. $\endgroup$
    – Lukas Lang
    Jul 3, 2021 at 13:21
  • $\begingroup$ Very nice suggestions and discussion. Thank you. $\endgroup$ Jul 3, 2021 at 22:32
2
$\begingroup$

You can take advantage of $CellContext` and the cell option CellContext:

Remove@x;
Begin["MyContext`"];
x = 123;
CellPrint[
 ExpressionCell[
  Manipulate[x + y, {y, 0, 1}],
  "Output",
  CellContext -> Context[]
  ]
 ]
End[];
x = 456;   (* does not change the value of Manipulate output *)
MyContext`x = 789; (* changes the value of Manipulate output *)

Caveat:

If the output cell generated above is copied and pasted, all works fine.

If only the Manipulate in the output cell is copied and it is pasted into an existing cell, then the context of that cell will be used. Normally the cell context is Global` .

Alternate method

In this alternative, Manipulate sets the cell context. If the Manipulate output is copied and pasted into another cell, it will overwrite that cell's context to "MyContext`".

Begin["MyContext`"];
`x = 123; (* Lukas Lang's suggestion *)
With[{context = Context[]},
 Manipulate[x + y, {y, 0, 1},
  Initialization :> (CurrentValue[EvaluationCell[], CellContext] = 
     context
    )
  ]
 ]
End[];
$\endgroup$
3
  • $\begingroup$ Thank you. Your "alternate method" requires also Remove@x in the beginning. Otherwise x stays symbolic in the output of Manipulate. I wonder what would be an elegant way to deal with this without removing x from "Global`". Can you think of alternatives to the suggestion of Lukas Lang? $\endgroup$ Jul 2, 2021 at 23:24
  • 1
    $\begingroup$ @MathGaudium Yeah, I had Remove@x in my code on my machine, but thought it would be clear if I just highlighted the changes. Not sure why I thought that now. The need for Remove comes down to how packages work. New variables are created in the current context; old ones keep their contexts. You can write MyContext`x = 123 in the external code. Or you can change CellContext of the Begin...End[] input cell to "MyContext`". $\endgroup$
    – Michael E2
    Jul 3, 2021 at 0:57
  • $\begingroup$ Thanks for your explanation again. It would be really good if there would be a way to "open" a context and have variables be assigned in this context regardless of whether they exist in the Global context or not. $\endgroup$ Jul 3, 2021 at 1:11
1
$\begingroup$

Manipulate is a Dynamics module and I do not think the Context you are setting affects it.

One can see this by doing Print[Context[]]; from inside Manipulate. The context you are setting do not extend to it:

Begin["MyContext`"];
Print[Context[]]; (*this prints MyContext`*)
Manipulate[
 Print[Context[]]; (*this prints Global`*)
 x = 100;
 Print["the above x sits in context ", Context[x]]; (*this prints Global`*)
 y,
 {{y, 0, "y"}, 0, 1, .1}
 ]
End[];

enter image description here

But if you want to modify the x used Manipulate from outside, Why not just leave it global?

ClearAll[x, y];
Manipulate[
 Grid[{{"x", x}, {"x+y", x + y}}, Frame -> All],
 {{y, 0, "y"}, 0, 1, .1},
 TrackedSymbols :> {x, y}
 ]

And now from outside Manipulate you can do x = 99; and Manipulate will update.

enter image description here

btw, I would not do any of these things myself. You are just asking for trouble.

Better to have all the variables to be controlled inside Manipulate and not make it use Global variables to update, and use a Module inside Manipulate to protect non-controlled variable from outside. That is the standard way to use Manipulate. You can also use its Initialization section to initialize things if needed.

$\endgroup$
1
  • $\begingroup$ If you put "Begin["MyContext`"];" in a separate cell, Print[Context[]] inside Manipulate actually does print "MyContext`". $\endgroup$ Jul 2, 2021 at 22:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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