# Subtle difference in conditional rules placement

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

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.

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

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[]],
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[]]],


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

h:

SetDelayed[
Condition[h[Pattern[x, Blank[]]],


Same as f, goodbye Unevaluated

-
I see you had a quite different analysis, and it appears to be the correct one. –  Mr.Wizard Mar 19 '12 at 21:52
Interestingly, I think my observation is still valid, even if it does not directly apply here. –  Mr.Wizard Mar 19 '12 at 21:53
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 –  Rojo Mar 19 '12 at 21:57
I'm a bit puzzled by the sentence "Strips Evaluate, evaluates the arguments with it". Which Evaluate? –  Sjoerd C. de Vries Mar 19 '12 at 22:14
@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 –  Rojo Mar 19 '12 at 22:15

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.

-