10
$\begingroup$

In most languages, any variable defined inside a function is considered local.

# a Python function
def toRomanNumerals(num):
    digits = # ...
    string = ''
    while num > 0:
        (value, letters) = # ...
        string = # ....
    return string

In a Mathematica Module, however, we must specify a list of all variables to treat as local.

toRomanNumerals[i_Integer?Positive] := 
 Module[{num = i, string = "", value, letters, digits}, 
  digits = (* ... *);
  While[num > 0, {value, letters} = 
    (* ... *);
   string = (* ... *).];
  string]

I sometimes find it tedious to manually type the list of all local variables. Do others feel the same way?

Is there a way to create a Module which treats all symbols defined inside as local?

$\endgroup$
8
  • 1
    $\begingroup$ There is a big difference between Python of Mathematica (one of many): Mathematica is a symbolic language where a symbol may hang around without having any value, and this is perfectly legal. In Python, it is clear which symbols to treat as local: all currently unbound symbols. But In Mathematica, this is not necessarily the case. You can implement Python-style bindings using macros, but then you will have to adopt the same constraint, that by local you want to consider all currently unbound variables. $\endgroup$ Nov 27, 2013 at 20:18
  • $\begingroup$ To give an example in the spirit of Leonid's comment: Module[{}, f[x]]. Should f and x be local or global? Perhaps you say that f is global if f has associated rules ("it's defined"), otherwise local, and the same applies to x. But what if x has a special meaning in the definition of f, but it doesn't have any associated rules of its own? Say, f[big]=1; f[small]=0 where small and big are symbols without definitions, but are used for a specific purpose. Even some builtins like $Pre don't have a definition by default. $\endgroup$
    – Szabolcs
    Nov 27, 2013 at 20:25
  • $\begingroup$ In most languages, any variable really? What about Ada, Pascal, C, C++, Java, SNOBOL, PLI, COBOL? don't you have to declare variable? I much prefer Mathematica way. It is a form of documentation. One can look at the list of local variable and know which are local or which are not. Languages that do not require declaration are sloppy languages IMNSHO. ofcourse, in Mathematica, you do not have to declare local symbols as local, but this is bad, since they will become global. I think Mathematica got it right here. $\endgroup$
    – Nasser
    Nov 27, 2013 at 20:27
  • 1
    $\begingroup$ @Nasser I guess what he means that in languages where you don't need to declare a variable, the variable will typically live in the scope where it's mentioned first; unless it's explicitly made global. Mathematica doesn't require declaration either (you can use any symbol anywhere), but the symbol will be global by default, unless you ask to make it local. $\endgroup$
    – Szabolcs
    Nov 27, 2013 at 20:36
  • $\begingroup$ @Szabolcs but OP said In most languages, any variable declared inside a function is considered local. and I was replying to this. But any way. I myself do not languages that makes things implicit like this. I prefer explicit declarations. Much better in the long run. It helps build better software and better habit to learn to declare something before using it. $\endgroup$
    – Nasser
    Nov 27, 2013 at 20:44

1 Answer 1

6
$\begingroup$

This sort of programming is not my strength and I don't know Python. Reading the comments, perhaps this won't quite be perfect, but it might be good enough. It seems good enough for many purposes, at least in the way I interpret the question. It localizes all Symbols in the code that are in the "Global`" context and don't have Ownvalues or DownValues. If you want to exclude those with UpValues etc., then it should be easy how to modify it to do so.

ClearAll[localizeAll];
SetAttributes[localizeAll, HoldAll];
localizeAll[code_] := 
  With[{vars = Join @@ Union @
    Cases[Hold[code], 
          s_Symbol /; Context[s] == "Global`" &&
             Length@OwnValues[s] == 0 && Length@DownValues[s] == 0 :> Hold[s],
          Infinity,
          Heads -> True]},
   (* Module[{##}, code] & @@ vars *)  (* original *)
   vars /. Hold[v___] :> Module[{v}, code] (* Leonid Shifrin's suggestion *)
   ]

Example:

Clear[z];
z[a_] := a^2;
localizeAll[x = 2; y = x++; z[x] = y; q[r_] := s; z[2x]]
z[3]
z[x]
q[1]

(* 36 *)
(* 2 *)
(* x^2 *)
(* q[1] *)

Note that because z was defined, z[3] got redefined.

I think I might rather take a few seconds and type out the variables explicitly. It might save time in the long run.

$\endgroup$
4
  • $\begingroup$ This looks similar to my code here. An interesting variation. $\endgroup$
    – rcollyer
    Nov 28, 2013 at 4:32
  • $\begingroup$ @rcollyer Yes, the idea is similar. Yours looks more sophisticated, but frankly, I don't have the experience to confidently judge the shortcomings my own code. $\endgroup$
    – Michael E2
    Nov 28, 2013 at 5:15
  • 1
    $\begingroup$ I don't have the time to test right now, but it seems that your code will leak evaluation at the stage Module[{##},body]&@@.... Otherwise, that's also how I would have done that (and actually did a number of times, just can't find my posts on this right now). You can use injector pattern to avoid the leaks: vars/.Hold[x___]:>Module[{x},code] - this is what I usually do in such cases. +1. $\endgroup$ Nov 28, 2013 at 12:03
  • $\begingroup$ @LeonidShifrin Thanks. I incorporated your suggestion. $\endgroup$
    – Michael E2
    Nov 29, 2013 at 3:49

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