4
$\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$
  • $\begingroup$ The local MainVar symbol "true name" isn't mainVar. Try removing the assignment MainVar = 3; to see it. $\endgroup$ – Dr. belisarius Aug 17 '14 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 Aug 17 '14 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$ – NicoDean Aug 17 '14 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$ – seismatica Aug 17 '14 at 17:43
5
$\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$
  • $\begingroup$ thanks, very interesting! Especially the hint to Block. $\endgroup$ – NicoDean Aug 17 '14 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 Aug 17 '14 at 16:57
  • $\begingroup$ already found that question and its answers some minutes ago. :) $\endgroup$ – NicoDean Aug 17 '14 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 Aug 17 '14 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 Aug 17 '14 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$
  • $\begingroup$ Very interesting, especially to resolve my confusion about why i dont see the $nnn. Thanks! $\endgroup$ – NicoDean Aug 17 '14 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$
  • $\begingroup$ Thanks for introducing me to $ModuleNumber! $\endgroup$ – seismatica Aug 18 '14 at 1:00

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.