4
$\begingroup$

If I execute a line of code with Evaluate I find some unexpected behavior when the code returns an error. Here is a minimal example. Let f

f[x_, y_] := (WriteString["stdout", "Hi "];
              y /. x);

Now if I call

In[1]:= Evaluate[f[a->1,a]]

Hi

Out[1]= 1

As expected, while if I do

In[2]:= Evaluate[f[a,b]]

this obviously results in an error (because a is not a Rule), but I can see that the function is evaluated multiple times

Hi Hi Hi

ReplaceAll: {a} is [etc...]

Out[2]= b/. a

As you see it is evaluated three times. The same instruction without the Evaluate returns the same but with only one "Hi."

I suspect that in order to throw the error Mathematica is enclosing that statement inside a held function and the Evaluate is causing it to evaluate again. But I can't figure out what's happening in detail.

$\endgroup$
4
$\begingroup$

The issue is caused by the top-level Evaluate interacting with the HoldForm wrappers returned by MessageMenu`Dump`$GetStack (which is defined as Stack[_]). This function is called twice when building the error message, explaining the two additional calls.

How to find this

We start by changing the definitions of the question a bit to make tracking easier:

g[] := (WriteString["stdout", "Hi "]; marker)

f[x_, y_] := (g[];
   y /. x);

Now we Trace the problematic call, using Unevaluated to protect the Evaluate from being evaluated too early. We also need TraceInternal->True. The trace is filtered for calls to g and TraceAbove->True is used to show the stack leading up to the call:

Trace[Unevaluated@Evaluate[f[a, b]], g, TraceInternal -> True, 
 TraceAbove -> True]
(* long trace... *)

We can now search for marker using the search functionality (this indicates that g has actually been evaluated. We find three occurences:

enter image description here enter image description here enter image description here

| improve this answer | |
$\endgroup$
  • 2
    $\begingroup$ Other tools: (1) On[HoldForm], though the printing of the trace messages causes even more greetings; one can inspect the stack. (2) This makes the role of HoldForm a bit clearer: f[x_, y_] := (Print[Stack[_HoldForm]]; y /. x); (3) This gets rid of the problem: Unprotect@HoldForm; SetAttributes[HoldForm, HoldAllComplete]; Protect@HoldForm;, though I don't like changing system functions. (+1) $\endgroup$ – Michael E2 Aug 15 '19 at 19:09

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.