The action of the symbol Unevaluated
is due to a special rule within the evaluator that occurs at a very specific time during the evaluation process. Unevaluated
only suppresses the evaluation of an expression part if it appears explicitly as the head of that part prior to the part's evaluation. Once the part has been evaluated, it is too late to suppress evaluation. If a part's evaluation result happens to be an expression with the head Unevaluated
, the evaluator will use the result as-is.
The consequence of this for the case at hand is that the Length
function actually receives the expression Unevaluated[a-a]
instead of a-a
. The length of the former expression is 1
, just as Length[Hold[a-a]]
is 1
.
The detailed sequence of events is as follows:
- The head of the expression,
Length
, is found to be inert and is used as-is.
Length
has no special attributes like HoldFirst
or HoldAll
, so the expression's first part will be evaluated.
- The first part,
Apply[Unevaluated, Hold[a-a]]
is checked to see if it is of the form Unevaluated[...]
. It is not, so the evaluator is going to evaluate it.
- The first part is now evaluated in turn, yielding
Unevaluated[a-a]
. Unevaluated
has the attribute HoldAllComplete
so its argument a-a
remains unevaluated during this recursive evaluation. The evaluator does not care at this point that the head of the result is Unevaluated
-- it is too late to avoid evaluating the part.
- The built-in definition of
Length
is applied to its argument, Unevaluated[a-a]
. The result is 1
since the expression has only a single subpart, a-a
.
The steps of the standard evaluation sequence can be found in Wagner's Power Programming With Mathematica. See (22724) to obtain a free copy. The relevant evaluation rule can be found on page 192, section 7.1.3, step 7b.
Work-arounds
As work-arounds, we can use alternatives that produce the desired Unevaluated[...]
expression prior to passing it to Length
:
With[{e = Unevaluated @@ Hold[a - a]}, Length[e]]
or
Length @@ {Unevaluated @@ Hold[a - a]}
or
Construct[Length, Unevaluated @@ Hold[a - a]]
or
Unevaluated @@ Hold[a - a] /. e_ :> Length[e]
(* 2 *)
Intrepreting Trace
Output
Trace
does not reliably show all of the substeps and special rules that occur during evaluation. But in the case at hand we can see some subtle clues about what is happening. First, consider this trace:
Trace[Length[Unevaluated[a-a]]]
(* {Length[a-a], 2} *)
Notice how Unevaluated
does not appear at all in this trace. The special rule has been quietly applied. This is an example of an evaluation substep that does not appear in the trace. Only explicit evaluation inputs or outputs appear. We can see that Length
receives the argument a-a
which has length 2.
Now consider this trace:
Trace[Length[Unevaluated @@ Hold[a - a]]]
(* {{Unevaluated@@Hold[a-a], Unevaluated[a-a]}, Length[Unevaluated[a-a]], 1}*)
This time, we see Unevaluated
appearing several times. That is because there are expressions containing it that are explicitly evaluated. We can see that Length
receives the argument Unevaluated[a-a]
which has length 1.
Length[#] &[Unevaluated @@ Hold[a - a]
does give 2. $\endgroup$