6
$\begingroup$

I encountered an unexpected behaviour in my mathematica-code, and want to understand whether that's a mathematica bug, or a misinterpretation on my side.

MainVar = 1;
Carrier = "MainVar";
f[] := (
  Module[{MainVar}, 
    MainVar = 3;
    Print[MainVar];
    Print[Carrier];
    Print[ToExpression[Carrier]]
])
f[] (* 3, MainVar, 1 *)

I would have expected that Print[ToExpression[Carrier]] gives me 3, because Carrier points to MainVar, which has the value 3.

When I use MainVar as global variable (as Eldo suggested below), I get the expected 3. So my question is:

Why does ToExpression[Carrier] give me 1 when I use MainVar as a local variable, even if both MainVar and Carrier have the correct values?

$\endgroup$
4
  • $\begingroup$ The local MainVar symbol "true name" isn't mainVar. Try removing the assignment MainVar = 3; to see it. $\endgroup$ Commented Aug 17, 2014 at 16:37
  • $\begingroup$ You can also put Print[SymbolName@Unevaluated@MainVar] inside the module to see what the real local name of MainVar is inside Module. $\endgroup$
    – Michael E2
    Commented Aug 17, 2014 at 16:41
  • $\begingroup$ belisarius: wow i didnt know that. But I still dont understand why within the Module, Mathematica still uses the global one instead of the local one? I'm confused especially because Print[MainVar] uses the local one. $\endgroup$ Commented Aug 17, 2014 at 16:42
  • 1
    $\begingroup$ I wonder if there's a way to get the correct result while still keeping the Module. Might be fun to think about but I can't seem to find one method that would work. $\endgroup$ Commented Aug 17, 2014 at 17:43

3 Answers 3

6
$\begingroup$

Start here:

Module works by replacing explicit appearances of a given Symbol with a different one with a derived name, e.g.:

Module[{x}, x]
x$715

Since MainVar appears nowhere in Print[ToExpression[Carrier]] the Module will not affect it.

A far simpler example of the same behavior that affects your case:

Module[{foo = 7}, ToExpression["foo"]]
foo

If you were to use Block instead you would temporarily change the value of MainVar which will have the desired effect:

f[] :=
 Block[{MainVar},
   MainVar = 3;
   Print[MainVar];
   Print[Carrier];
   Print[ToExpression[Carrier]]
 ]

f[]

3

MainVar

3

$\endgroup$
6
  • $\begingroup$ thanks, very interesting! Especially the hint to Block. $\endgroup$ Commented Aug 17, 2014 at 16:52
  • $\begingroup$ @NicoDean You're welcome. See especially WReach's answer in the linked Q&A; it is the most concise demonstration of With/Module/Block behavior I have seen. $\endgroup$
    – Mr.Wizard
    Commented Aug 17, 2014 at 16:57
  • $\begingroup$ already found that question and its answers some minutes ago. :) $\endgroup$ Commented Aug 17, 2014 at 16:58
  • 2
    $\begingroup$ @NicoDean Be careful though not to just blindly swap Module for Block. See MrWizard's links and see how Block can unexpectedly bite you when used the wrong way ... $\endgroup$
    – Szabolcs
    Commented Aug 17, 2014 at 17:16
  • 1
    $\begingroup$ @NicoDean Very true what Szabolcs wrote! When I was first using Mathematica I used Block all the time after somehow convincing myself it was "better" without actually understanding the ramifications of my action. Don't make my mistake. $\endgroup$
    – Mr.Wizard
    Commented Aug 17, 2014 at 17:27
5
$\begingroup$

The reason for this behaviour is that Module does localization by renaming. For example:

Module[{x}, x]
(* x$982 *)

That x inside Module is renamed to something like x$nnn with nnn being a different and unique number every time Module is evaluated. Module will not be able to do the renaming inside any strings, so ToExpression["MainVar"] will evaluate to MainVar and not to MainVar$nnn.

We don't normally see these x$nnn forms because they have the Temporary attribute, which means that they automatically disappear as soon as they are not referenced any more (which is usually when the Module finishes evaluating). They can however be easily returned from a Module as above.


General advice: avoid ToExpression if you can. Mathematica expressions are flexible enough that it is almost never necessary to manipulate code as a string. You can use held expressions instead.

$\endgroup$
1
  • $\begingroup$ Very interesting, especially to resolve my confusion about why i dont see the $nnn. Thanks! $\endgroup$ Commented Aug 17, 2014 at 16:53
4
$\begingroup$

Another resource related to this question is the tutorial

How Modules Work

It explains how, as others have pointed out, the symbol that is actually created in

Module[{x}, x]

is x$nnn, where nnn is the current value of $ModuleNumber. $ModuleNumber is increased whenever Module is called (with local variables) and at other times.


There has been some curiosity about whether one can get at the local variable using ToExpression. One can take advantage of $ModuleNumber to do this, but it is hard to imagine it would be worth working around the difficulties. In every case, I would think it should be possible to avoid having to do this with little inconvenience.

So just to satisfy curiosity here is a way:

MainVar = 1;
Carrier = "MainVar";
f[] := Module[{MainVar},
  MainVar = 3;
  ToExpression[Carrier <> "$" <> ToString[$ModuleNumber - 1]]]

f[]
(* 3 *)

You have to subtract 1 from $ModuleNumber because it gets increased right after the variable(s) (only MainVar in this case) are instantiated.

But here's where it gets tricky. You have to make sure that $ModuleNumber hasn't been increased when you use it. Many internal functions use Module. For instance, consider this:

MainVar = 1;
Carrier = "MainVar";
f[] := Module[{MainVar},
  Print[SymbolName@Unevaluated@MainVar];
  Print[$ModuleNumber];
  MainVar = Integrate[x^2, {x, 0, 1}];
  Print[$ModuleNumber];
  ToExpression[Carrier <> "$" <> ToString[$ModuleNumber - 1]]]

f[]
(*
  Print: MainVar$328200
         328201
         328218
  Out[]= MainVar$328217
*)

One way to work with it is to store the current module number at the start:

MainVar = 1;
Carrier = "MainVar";
f[] := Module[{MainVar, myModuleNumber = $ModuleNumber},
  Print[myModuleNumber];
  Print[$ModuleNumber];
  MainVar = Integrate[x^2, {x, 0, 1}];
  Print[$ModuleNumber];
  ToExpression[Carrier <> "$" <> ToString[myModuleNumber]]]

f[]
(*
  Print: 328221
         328222
         328229
  Out[]= 1/3
*)
$\endgroup$
1
  • $\begingroup$ Thanks for introducing me to $ModuleNumber! $\endgroup$ Commented Aug 18, 2014 at 1:00

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.