2
$\begingroup$

I encountered a very strange behaviour on Mathematica using Evaluate on a held argument. Could anyone explain to me what is the subtlety (or is it a bug?)

Here is the expression:

 ClearAll[evaluateForThreeParameters, myParameter, setMyParameter];
 setMyParameter[p_] := (myParameter = p); 
 evaluateForThreeParameters[form_] := 
 Table[(setMyParameter[p]; Evaluate[form]),
       {p, {1, 2, 3}}];
 SetAttributes[evaluateForThreeParameters, HoldFirst];
 setMyParameter[thisShouldNotAppear];
 evaluateForThreeParameters[myParameter^2]

I would expect that the last expression should yield {1, 4, 9} but instead Mathematica (version 11.0.1.0 on MacOSX) evaluated this to: {thisShouldNotAppear^2, 1, 4}.

And what is even more strange, it is that if I embed the expression Evaluate[form] above inside the Identity function, which should change nothing, then I get the expected answer {1,4,9}, i.e. when I evaluate the expression

ClearAll[evaluateForThreeParameters, myParameter, setMyParameter];
setMyParameter[p_] := (myParameter = p); 
evaluateForThreeParameters[form_] := 
Table[(setMyParameter[p]; Identity[Evaluate[form]]),
      {p, {1, 2, 3}}];
SetAttributes[evaluateForThreeParameters, HoldFirst];
setMyParameter[thisShouldNotAppear];
evaluateForThreeParameters[myParameter^2]

Could anyone clarify what I am missing ? Thanks, Pascal

$\endgroup$
3
$\begingroup$

Short Version

The behaviour we see is by design. It is due to the fact that we are using the special head Evaluate to force early evaluation of the second argument of CompoundExpression -- an argument which would otherwise be held until its time came to be evaluated. If we wrap Identity around Evaluate, then this special action is prevented.

Details: A Simpler Case

We can see this behaviour using simpler expressions. First, consider the ordinary use of CompoundExpression, whose shorthand operator form is ;:

Print["one"]; Print["two"]

(* one
   two *)

No surprises here. But if we wrap the second expression within Evaluate, it is evaluated before the compound expression itself is evaluated:

Print["one"]; Evaluate[Print["two"]]

(* two
   one *)

After that early evaluation and two is printed, the compound expression is effectively reduced to this:

Print["one"]; Null

(* one *)

Evaluate is a distinguished head that has special meaning built into the evaluator. It causes its argument to be evaluated before the outer form itself, even though the outer form would otherwise hold the argument. CompoundExpression is such a form because it has the HoldAll attribute:

Attributes[CompoundExpression]

(* {HoldAll, Protected, ReadProtected} *)

The evaluation process is detailed in Power Programming with Mathematica by David B. Wagner. For the present case, see point 7(a) in section 7.1.3 on page 191.

Evaluate is only meaningful if it is directly contained within an expression which holds its argument. Otherwise, it is essentially equivalent to Identity. This is why the out-of-order evaluation is prevented if we wrap an explicit Identity around Evaluate:

Print["one"]; Identity[Evaluate[Print["two"]]]

(* one
   two *)

Note how Evaluate is no longer a top-level argument to CompoundExpression.

Original Case From The Question

We can now see what happens in the Table expression from the question:

setMyParameter[thisShouldNotAppear];
Table[(setMyParameter[p]; Evaluate[myParameter^2]), {p, {1, 2, 3}}]

(* {thisShouldNotAppear^2, 1, 4} *)

The presence of Evaluate is causing myParameter^2 to be evaluated before setMyParameter[p]. Thus, the results all appear to be "out by one" position.

A close inspection of the evaluation trace will show that the compound expression has already changed to setMyParameter[p];thisShouldNotAppear^2 prior to its first evaluation, and is setMyParameter[p];1 and setMyParameter[p];4 in the subsequent evaluations:

setMyParameter[thisShouldNotAppear];
TracePrint[Table[(setMyParameter[p]; Evaluate[myParameter^2]), {p, {1, 2, 3}}]]

(* large output omitted *)

The introduction of Identity prevents this premature evaluation so we get the otherwise expected results:

setMyParameter[thisShouldNotAppear];
Table[(setMyParameter[p]; Identity[Evaluate[myParameter^2]]), {p, {1, 2, 3}}]

(* {1, 4, 9} *)

A simpler fix is to remove Evaluate altogether as it is not needed here:

ClearAll[evaluateForThreeParameters, myParameter, setMyParameter];
setMyParameter[p_] := (myParameter = p);
evaluateForThreeParameters[form_] := Table[(setMyParameter[p]; form), {p, {1, 2, 3}}];
SetAttributes[evaluateForThreeParameters, HoldFirst];
setMyParameter[thisShouldNotAppear];
evaluateForThreeParameters[myParameter^2]

(* {1, 4, 9} *)
$\endgroup$
  • $\begingroup$ Thank tou so much for this neat answer (and for advertising Wagner's book I didn't know). I did not understand that Evaluate was useless here which is the clean fix. $\endgroup$ – pascal the algebraictopologist Jan 8 at 6:24

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.