6
$\begingroup$

Per comments, including here the description of the problem lifted from another site:

Mathematica Pure Function Scope Problem

Consider this snippet:

f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]

Executing this sequence generates errors messages:

Function::flpar: Parameter specification {0} in Function[{0},999] should be a symbol or a list of symbols. (x3)
$RecursionLimit::reclim: Recursion depth of 256 exceeded. (x4)

Hold[Function[{0},999][0]]

However, if you change the name of the function variable in the first line from a to z, then all works as expected:

f[x_] := g[Function[z, x]];
f[999]

=> 999

Another workaround is to compile the function:

f[x_] := g[Compile[a, x]];
f[999]

=> 999

The problem appears to be caused by a name conflict between the pure function's argument a and the helper function h's argument a. This is a really nasty problem because the definitions are completely separate -- one would have to perform a global code analysis to turn up such problems.

I'm a relatively new user of Mathematica and have already ran into that problem, asked and answered here.

I'm used to C++; in C++ and most other programming languages) local scope of function arguments and local scope of most variables prevents variable name clash between different functions.

Thus the question: how does one prevent the above problem in Mathematica?

$\endgroup$
  • 1
    $\begingroup$ It may seem a pain, but please make the question self-contained. $\endgroup$ – Michael E2 Sep 26 '16 at 23:18
  • $\begingroup$ @MichaelE2: OK. $\endgroup$ – Michael Sep 27 '16 at 0:28
  • $\begingroup$ Related: How does Table behave differently? $\endgroup$ – Karsten 7. Sep 27 '16 at 3:39
  • 1
    $\begingroup$ f[999] // Trace // Quiet makes clear what's going on, I think, for those who can read Trace output. Both a are renamed to prevent clashes, but renamed to the same thing a$, which causes the clash. Bummer. This language area is not my forte, but it looks like a bug. $\endgroup$ – Michael E2 Sep 27 '16 at 10:29
6
$\begingroup$

This question asks about two distinct problems, with different remedies.

Problem #1: "Mathematica Pure Function Scope Problem"

I encountered this problem in a non-trivial body of Mathematica code and it took me hours to isolate it and fix it. I reported the issue to WRI at the time. They acknowledged the bug and suggested that the work-around is to avoid the use of named arguments in pure functions. That is, rewrite Function[{x, y}, ... x ... y ...] as ... # ... #2 ... &. I now avoid named arguments in pure functions like the plague on account of the trauma of the whole experience.

At first this sounds like an old joke (Patient: It hurts when I do this. Doctor: Don't do that.), but I find in practice is rarely causes me any grief. The rare exception is when I wish to nest two pure functions and want to inject a value into the inner scope (for examples, see (16947) or (38393)). The usual work-around is to use named arguments, e.g.

Function[{x}, ... Function[{y}, ... x ...] ...]

but since this is plague-infested I usually just create a regular (non-pure) named function or look for an alternate technique such as using replacement rules. Again, I emphasize that I find such cases to be very rare.

Problem #2: Free Variables Within Function Definitions

The second problem from the linked question concerns the errors arising from the following expressions:

f[a_, b_, c_] := Integrate[E^(-x^2 - y^2), {x, -a, -a + c}, {y, -b, x - b + a}] / Pi

Plot3D[f[x, y, 10], {x, -3, 13}, {y, -3, 13}]

(* ... many errors ... *)

In this case, the problem lies with the definition of f. It references the free variable symbols x and y. When the function evaluates, these symbols will take on whatever values they happen to have in the enclosing scope. In the case at hand x will take on various values between -3 and 13. But Integrate wants x to be a variable name, not some real value. Hence the errors.

The fix is to use Module to localize x and y:

f[a_, b_, c_] :=
  Module[{x, y}
  , Integrate[E^(-x^2 - y^2), {x, -a, -a + c}, {y, -b, x - b + a}] / Pi
  ]

(but note the comments on the linked question: this multidimensional integral is computationally expensive to evaluate and one would be wise to find another method to perform the integration)

I think it is fair so say that most users are surprised sooner or later by the need to localize the integration variables in Integrate and the iterators in functions like Table, Plot, etc. This issue is discussed at length in (94061) and (126683), including discussion about the Mathematica design decisions that make such localization necessary in the first place.

$\endgroup$

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.