Problem with Unevaluated

I found this problem form Robby Villegas's Working with Unevaluated Expressions, here is the problem i copied from his notebook:

The subtle and confusing situation where Unevaluated persists is when an argument did not originally have a head of Unevaluated, but became Unevaluated[whatever] after argument evaluation finished.

A simple example:

Print[ToExpression["Unevaluated[1 + 2]"]

This prints Unevaluated[1+2]. I would have expected 1+2 to be printed.

Why does this happen?

• What is your question exactly? Jan 26 '20 at 15:50
• @Szabolcs From my understanding about the principle of Mathematica Expression evaluation the Unevaluated[1+2] should discard the Head and give the result 1+2 in the above example.
– 任天一
Jan 26 '20 at 16:15

I believe this happens because Unevaluated (along with some other special constructs such as Evaluate and Sequence) should not be thought of as evaluating in a certain way. What they actually do is influence how their enclosing expression is evaluated. More precisely, they influence how their enclosing expression will treat its arguments.

An example:

Assume f has the HoldFirst attribute and we enter:

f[Evaluate[1+1], 2+2, Unevaluated[3+3]]

The following happens:

1. Mathematica scans the expression starting from the outermost level. It first looks at f, and decides what to do with each of its arguments: should they be evaluated or not?
2. Once this decision is made, the arguments marked for evaluation will be evaluated. The ones marked to be held are left as they are.
3. Finally, f itself is evaluated.
4. If f did not itself need to be evaluated, the Unevaluated wrapped is restored.

Wrappers like Evaluate and Unevaluated are handled in step 1. and not in step 2. Their effect is applied in step 1 and they are immediately removed, before proceeding to step 2.

Thus, Mathematica first looks at f's first argument: Since f is HoldFirst, it would not normally be marked for evaluation. But it sees Evaluated. Evaluated is now stripped and its contents are marked for evaluation. Now it looks at the second argument, 2+2. It decides that this will need evaluation. Finally, it looks at the third argument: Unevaluated is stripped, and its contents are marked to be left alone. So the decision is

• 1st argument: evaluate
• 2nd argument: evaluate
• 3rd argument: don't evaluate

Now the evaluation happens: 1+1 -> 2, 2+2 -> 4, 3+3 -> 3+3 (left alone)

Finally, f itself is evaluated with these results.

Example:

SetAttributes[f, HoldFirst]

f[Evaluate[1 + 1], 2 + 2, Unevaluated[3 + 3]]
(* f[2, 4, Unevaluated[3 + 3]] *)

Notice that Unevaluated was restored, as no evaluation needed to be done for f itself. If f had a definition, as below, the Unevaluated would not be restored:

f[args___] := Hold[args]

f[1 + 1, 2 + 2, 3 + 3]
(* Hold[1 + 1, 4, 6] *)

Thus, Print[Unevaluated[1+2]] works like this:

1. Strip the Unevaluated and mark 1+2 to be left alone
2. 1+2 is marked as held, so no evaluation necessary
3. Print prints the expression it received: 1+2

Print[ToExpression["Unevaluated[1+2]"]] works like this:

1. The argument of Print is marked for evaluation
2. ToExpression["Unevaluated[1+2]"] is evaluated and results in Unevaluated[1+2]
3. Print prints the expression it received: Unevaluated[1+2]

The explanation here omits several steps. To see the entire evaluation procedure, take a look at "Mathematica Internals" by David Withoff. • I believe this explanation, while being mostly correct, is missing the crucial (for answering the question) point that the Unevaluated wrappers get restored at the end, in case if no non-trivial evaluation took place. In David Withoff's "Mathematica Internals" tech. report from 1992, it stands as a separate step in the main evaluation sequence: "Restore the head Unevaluated if no applicable rules were found.". Jan 26 '20 at 16:44
• @LeonidShifrin Thank you for the reference! I was not able to find one. I added it now. Jan 26 '20 at 16:57
• @Szabolcs Thanks a million！i get it!
– 任天一
Jan 26 '20 at 17:05
• @Szabolcs sure, was happy to help. +1 :) Jan 27 '20 at 12:35