4
$\begingroup$

In my previous question I described a problem, when one of the approaches I tried to use didn't work because of the interaction between Evaluate[] in combination with SetDelayed[].

Suppose we have a symbol defined in a global scope:

x = 1;

And now we defined a function:

f[x_] := x + 1

In this case Mathematica understands that x refers to the function argument and not to the global symbol:

?f

(* Definitions: f[x_] := x + 1 *)

Now I want to define one function in terms of another:

f[x_, a_] := a x
f1[x_] := f[x, 1] + 1
?f1

(* Definitions: f1[x_] := f[x, 1] + 1 *)

As expected again x here refers to the function argument.

Now I want to simplify the definition of f1, so that it looks like:

(* Definitions: f1[x_] := 1 + x *)

and I use Evaluate[]:

f1[x_] := Evaluate[f[x, 1] + 1]

But instead of 1 + x I get:

(* Definitions: f1[x_] := 2 *)

Because when applying Evaluate[] to an expression containing x Mathematica thinks that I'm referring to a global x rather than the function argument.

Why does Evaluate[] work like this? Is there any way to override the scope of the x?

$\endgroup$
1
  • 1
    $\begingroup$ is this simply because the RHS of the function f is now evaluated immediately, and hence will use the global x, and not when the function f is called in order to use its argument x instead? In effect, writing f1[x_] := Evaluate[f[x, 1] + 1] is as if you have written f1[x_] = f[x, 1] + 1] countering the whole reason to use the delayed assignment,. $\endgroup$
    – Nasser
    Commented Nov 4, 2021 at 13:01

1 Answer 1

6
$\begingroup$

Don't think about these things in terms of "scope", as that will lead to confusion. Mathematica does not (usually) use "scope" in the way you may be familiar with from other languages. In particular, SetDelayed does not do any scoping at all*. All it does is hold off from evaluating the RHS. The symbol x you write there is exactly the same as the symbol x you called "global". The only difference is in when and how x is evaluated, but not in the identify of x.

But let's talk about how to fix your example. As you know, the problem is that x has a value. If x had no value, then f1[x_] := Evaluate[f[x, 1] + 1] would work as you want it to. One possible solution is to temporarily unset x's value. We do this with Block.

Block[{x},
 f1[x_] := Evaluate[f[x, 1] + 1]
]

Definition[f1]

(* f1[x_] := 1 + x *)

Block does dynamic scoping. Essentially, it temporarily removes all definitions from x.

Additional reading:


* To be accurate, Mathematica does do some scoping, and SetDelayed does interact with it. This is why we can't use Module instead of Block in my example. But discussing this here would distract from the main topic.

$\endgroup$
1
  • $\begingroup$ Great! Thank you. That's what I was looking for. I actually tried using Module in my tests but obviously it didn't work. $\endgroup$
    – Max
    Commented Nov 18, 2021 at 8:35

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.