10
$\begingroup$

I had gotten used to thinking of the placement of a condition in a rule definition as logically immaterial. In my specific example, namely

ClearAll[f,g,h];
SetAttributes[f,HoldAllComplete];
f[x_ /; Head@Unevaluated@x === Symbol] := x;

SetAttributes[g,HoldAllComplete];
g[x_] := x /; Head@Unevaluated@x === Symbol;

SetAttributes[h,HoldAllComplete];
h[x_] /; Head@Unevaluated@x === Symbol := x;

all three produce the same results when applied to the same symbol

In[13]:= f[foo] === g[foo] === h[foo] === foo

(* Out[13]= True *)

But now I try to get fancy and write the following

ClearAll[f, g, h, foo];
SetAttributes[f,HoldAllComplete];
f[x_ /; Head@Unevaluated@x === Symbol] := Unevaluated@x;

SetAttributes[g,HoldAllComplete]
g[x_] := Unevaluated@x /; Head@Unevaluated@x === Symbol;

SetAttributes[h,HoldAllComplete]
h[x_] /; Head@Unevaluated@x === Symbol := Unevaluated@x;

In[20]:= {f[foo], g[foo], h[foo]}

and the results are very different

(* Out[20]= {foo, Unevaluated[foo], foo} *)

I try to figure it out with traceView2; I can see the differences

traceView2 differences

but I get quickly lost in the documents trying to understand what, why, and how. Seems even this example is not so simple and probes dark (at least to me) corners of the evaluator. I'd be grateful for advice, explanations, ideas.

$\endgroup$
4
  • $\begingroup$ Related (possibly, even a dupe): mathematica.stackexchange.com/q/533/5 $\endgroup$
    – rm -rf
    Mar 19, 2012 at 21:29
  • $\begingroup$ @R.M Not really a dupe -- this has more to do with the behavior of Unevaluated itself. Working on an answer now. $\endgroup$
    – Mr.Wizard
    Mar 19, 2012 at 21:30
  • $\begingroup$ I love that traceview2 & 4 function. It is a lifesaver. I would +1 for the reference alone. $\endgroup$
    – tkott
    Mar 19, 2012 at 21:35
  • $\begingroup$ A relevant discussion: stackoverflow.com/questions/6267143/… $\endgroup$ Mar 19, 2012 at 23:14

2 Answers 2

11
$\begingroup$

This isn't a runtime issue, so you won't find the answer by tracing f[foo] and family.

It's a definition thing. If you look at the downvalues you'll see that in f as well as in h the Unevaluated got stripped off when running the SetDelayed

Let's look at the fullform of your definitions

f:

SetDelayed[
 f[Condition[Pattern[x, Blank[]], 
   SameQ[Head[Unevaluated[x]], Symbol]]],
 Unevaluated[x]]

So, Mathematica sees this code. First it evaluates SetDelayed, it is inert.

It checks its attributes: HoldAll, SequenceHold.

Now it strips off the Unevaluated of its arguments... (!!)

Evaluates the expression as a whole, and stores the definition (the Unevaluated is gone already)

g:

SetDelayed[
 g[Pattern[x, Blank[]]],
 Condition[Unevaluated[x], SameQ[Head[Unevaluated[x]], Symbol]]]

Now, in this case, no argument has head Unevaluated so it survives :)

h:

SetDelayed[
 Condition[h[Pattern[x, Blank[]]], 
  SameQ[Head[Unevaluated[x]], Symbol]], Unevaluated[x]]

Same as f, goodbye Unevaluated

$\endgroup$
16
  • 1
    $\begingroup$ I see you had a quite different analysis, and it appears to be the correct one. $\endgroup$
    – Mr.Wizard
    Mar 19, 2012 at 21:52
  • $\begingroup$ Interestingly, I think my observation is still valid, even if it does not directly apply here. $\endgroup$
    – Mr.Wizard
    Mar 19, 2012 at 21:53
  • 1
    $\begingroup$ Yeah, I agree. My answer is probably even confusing to someone that takes it as more general than it is intended... Yours shows the true spirit of Unevaluated $\endgroup$
    – Rojo
    Mar 19, 2012 at 21:57
  • $\begingroup$ I'm a bit puzzled by the sentence "Strips Evaluate, evaluates the arguments with it". Which Evaluate? $\endgroup$ Mar 19, 2012 at 22:14
  • $\begingroup$ @SjoerdC.deVries that was just a short attempt to be less particular. Makes no sense in this case because there's nothing to strip. I'm deleting it $\endgroup$
    – Rojo
    Mar 19, 2012 at 22:15
5
$\begingroup$

This highlights a subtle difference in Condition expressions but at its heart I believe is the behavior of Unevaluated, not Condition.

Consider first:

List[Unevaluated[1 + 1], "foo"]

Plus[Unevaluated[1 + 1], "foo"]
{Unevaluated[1 + 1], "foo"}

2 + "foo"

Unevaluated is only stripped when Mathematica determines that further evaluation is necessary (or perhaps conversely, it remains when Mathematica does not evaluate). Consider now:

List[#, "foo"] & @ Unevaluated[1 + 1]
{2, "foo"}

Because the pure function is not HoldAllComplete an additional evaluation is triggered that does not occur when using List directly.

You can strip down your question example to this observation:

ff := Unevaluated[1 + 1]

gg := Unevaluated[1 + 1] /; True

{ff, gg}
{2, Unevaluated[1 + 1]}

Apparently the use of Condition causes Mathematica to consider that the RHS expression has already been evaluated.

$\endgroup$
1
  • $\begingroup$ Nice find. That Unevaluated stays is not consistent with "tutorial/Evaluation" which says "Unless h has attribute HoldAllComplete, strip the outermost of any Unevaluated wrappers that appear in the Subscript[e, i]." but does not mention that this only happens when further evaluation is possible. $\endgroup$
    – masterxilo
    Jul 7, 2016 at 13:14

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.