11
$\begingroup$

I do not understand why Unevaluated sometimes does not work.

Consider this expression:

expr = Hold[a - a]

Now let's say I want to get the length of a-a which is 2. Then I would expect Length[Unevaluated@@expr] to give 2. But instead I get

Length[Unevaluated@@expr]
(*1*)

which suggests Length is failing to strip away the Unevaluated. Why?

If I inspect the evaluation with Trace

Trace[Length[Unevaluated@@expr]]
(*  {{{expr,Hold[a-a]}, 
       Unevaluated@@Hold[a-a],Unevaluated[a-a]},
         Length[Unevaluated[a-a]],1}  *)

The last couple steps (with Length[Unevaluated[a-a]] transforming to 1) looks wrong. After all, directly calling Length[Unevaluated[a-a]] gives 2.

What is happening with Trace?

$\endgroup$
2

1 Answer 1

18
$\begingroup$

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:

  1. The head of the expression, Length, is found to be inert and is used as-is.
  2. Length has no special attributes like HoldFirst or HoldAll, so the expression's first part will be evaluated.
  3. 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.
  4. 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.
  5. 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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.