It's instructive to look at the output of TracePrint
.
Length[Unevaluated[1 + 1 + 1 + 1]] // TracePrint
(* Out: *)
Length[Unevaluated[1+1+1+1]]
Length
Length[1+1+1+1]
4
Whereas
f[Unevaluated[1 + 1 + 1 + 1]] // TracePrint
(* Out: *)
f[Unevaluated[1+1+1+1]]
f
f[1+1+1+1]
Length[1+1+1+1]
Length
1+1+1+1
Plus
1
1
1
1
4
Length[4]
0
Let's try something similar, but different, before explaining this:
g = Length;
g[Unevaluated[1 + 1 + 1 + 1]] // TracePrint
(* Out: *)
g[Unevaluated[1+1+1+1]]
g
Length
Length[1+1+1+1]
4
What's going on here?
It has to do with the order in which Mathematica evaluates expressions. There's a fair bit I'm leaving out here, but here are the important details for this case. Consider an arbitrary expression h[x1, x2, ...]
, and let's walk through the evaluation process.
The head of the expression h
is evaluated as an expression in its own right (just as we would any other expression; that is, it is run through this process). Let's refer to whatever expression h
evaluates to (which most times, is simply h
itself) as h1
. (Note: arguments are not considered here. This is akin to putting h
in a cell by itself and hitting Shift+Enter.)
Each of the arguments x1
, x2
, ... is evaluated. If any of them begin with Unevaluated
, we strip off the Unevaluated
head, and then skip evaluation of that expression.
Apply any downvalues for h1
, i.e., rules that are used to transform the cases where h1
is given arguments. So, e.g., rules that are created by definitions like h1[x_] := ...
. Then, evaluate the resulting expression.
This process is recursive, so when we evaluate the resulting expression, we start over again at the top.
With this in mind, let's make sense of the previous evaluations.
Length[Unevaluated[1 + 1 + 1 + 1]]
. Length
evaluates to itself; Unevaluated
is stripped off; and we apply the definition (downvalue) of Length
to Plus[1,1,1,1]
directly, which is 4
. (Note that this downvalue for Length
is built-in, so you won't find it under DownValues[Length]
.)
f[Unevaluated[1 + 1 + 1 + 1]]
. f
evaluates to itself (as a bare expression—we're not applying to arguments yet!); Unevaluated
is stripped off; and we apply the downvalues of f
to Plus[1,1,1,1]
. Here, that downvalue is given by f[x_] := Length[x]
, so we substitute Plus[1,1,1,1]
into Length
to get Length[Plus[1,1,1,1]]
. Now, here's the crucial bit—since this came from a downvalue, we now evaluate that expression! In the previous example (Length[Unevaluated[1 + 1 + 1 + 1]]
), at this stage, we applied the (built-in) downvalue of Length
to Plus[1,1,1,1]
immediately to get 4
. But here, we're starting the evaluation process for this expression from the beginning again (step 1); so, we evaluate Length
, then evaluate Plus[1,1,1,1]
to get 4
, then evaluate Length[4]
, which is 0
.
g = Length; g[Unevaluated[1 + 1 + 1 + 1]]
. Here, we evaluate the head (g
) first. Since g
, by itself (no arguments), evaluates to the symbol Length
, we then proceed with the evaluation process with Length
in front: strip off Unevaluated
and skip evaluation of Plus[1,1,1,1]
, then see if there are any downvalues we can apply to the head of the expression. But, oh wait...the head has already been evaluated to Length
! And there is indeed a built-in downvalue for the head Length
, which gets us 4
(just as it did in (1) at this same stage).
So, the moral (and solution)?
Define the head to be Length
directly!
Since the head is always evaluated first, no matter what (unless your expression is, say, being examined by some other expression, which is preventing evaluation from happening normally), you can be safe defining g = Length
. This even applies for the more complicated things—upvalues, Hold*
attributes (even HoldAllComplete
!), and the rest. All that is only taken into consideration after the head of the expression, as an expression in its own right, is fully evaluated.
The only thing that could cause a problem is something preventing the expression (or g
) from evaluating normally, e.g. Hold[g[1+1+1+1]]
, some symbol with a Hold*
attribute, or something like Block[{g}, g[1+1+1+1]]
. For this, if you want a True alias in your notebook, you can define $Pre
to be a function which changes any occurrence of g
to Length
. $Pre
is a global variable applied to any input expression before any sort of evaluation of it begins whatsoever.
Let me know if any of this is unclear, or if you have further questions!
Alternatively: Now, let's say you want something more complicated—not just substituting one function for another. Maybe one function for a list of two others.
For that, we can exploit the attribute HoldAllComplete
. This intervenes immediately after the head is evaluated, and skips all evaluation of arguments (even upvalues), going directly to the step where we apply downvalues. So, for example,
ClearAll[f];
SetAttributes[f, HoldAllComplete];
f[x_] := {Length[x], Length[x]}
f[Unevaluated[1 + 1 + 1 + 1]]
(* Out: {4, 4} *)
HoldAllComplete
has caused the arguments to be immediately substituted into the downvalue definition. So, by "skipping right to the downvalue step", we've replicated Length
with downvalues instead of ownvalues—that is, with argument-taking definitions instead of something of the form f = Length
or f := Length
(which, by the way, would both work!)
Aside: There is another way to try to answer this question, which is to compare the definitions of the symbols via the undocumented function Language`ExtendedDefinition
or Language`ExtendedFullDefinition
. The problem is that this only helps us compare definitions for user-defined symbols, as the downvalues for Length
are built-in, and so don't have Mathematica-visible downvalues. (Try it—you'll get an empty DefinitionList
!) So, it's not necessarily relevant here, but I wanted to mention it as a way to inspect symbol behavior in certain contexts.
Unevaluated[#]&
different fromUnevaluated
? $\endgroup$