Preamble
This has been discussed before, and this problem was also identified and partially addressed in the same question. I will use a slightly simpler implementation which also covers UpValues
. It is probably not complete either, but it covers many common cases of interest.
Implementation
Here is the code:
ClearAll[symbolicHead];
SetAttributes[symbolicHead, HoldAllComplete];
symbolicHead[f_Symbol[___]] := f;
symbolicHead[f_[___]] := symbolicHead[f];
symbolicHead[f_] := Head[Unevaluated[f]];
ClearAll[valueQ];
SetAttributes[valueQ, HoldAllComplete];
valueQ[a_Symbol] /; OwnValues[a] =!= {} :=
With[{result = (# =!= (# /. OwnValues[a])) &[HoldComplete[a]]},
result /; result];
valueQ[a : f_Symbol[___]] /; DownValues[f] =!= {} :=
With[{result = (# =!= (# /. DownValues[f])) &@HoldComplete[a]},
result /; result];
valueQ[a_] :=
With[{sub = SubValues[Evaluate[symbolicHead[a]]]},
With[{result = (# =!= (# /. sub)) &[HoldComplete[a]]},
result /; result] /; sub =!= {}
];
valueQ[a_] :=
With[{upsyms =
Flatten@Cases[Unevaluated[a], s_Symbol :> UpValues[s], 1, Heads -> True]},
With[{result = (# =!= (# /. upsyms)) &[HoldComplete[a]]},
result /; result] /; upsyms =!= {}
];
valueQ[_] := False;
Symbolic heads are further discussed in this answer. The order of definitions is important, and roughly corresponds to the order of steps applying those global rules, in the main evaluation sequence.
Examples
a := Print["*"];
b[1] := Print["*"];
c[1][4] := Print["*"];
d /: f[x_Integer, d, y_Integer] := Print["*"];
valueQ /@ Unevaluated[{a, b[1], c[1][4], f[1, d, 2]}]
(*
==> {True, True, True, True}
*)
Limitations
I did not include the NValues
(this can be done, but the question is whether we really want to do that). This seems to pretty much exhaust the set of things we can do without really evaluating an expression in question. In certain cases, the results will be different from ValueQ
, for example:
{valueQ[N[Pi]],ValueQ[N[Pi]]}
(*
==> {False,True}
*)
Summary
The code above was not meant to be absolutely complete, and probably can not be, since not everything is exposed to the top-level / end user. But it is hoped that it covers many cases of interest, and can be further extended to cover some that it misses currently. Note that, since internal global rules are not available at the top-level, valueQ
is mostly limited to user-defined or top-level functions and variables. If one wants to include system symbols with internal rules, I don't see other ways than allowing the expression to evaluate.
This may also explain (to some extent), why built-in ValueQ
was written the way it was - to also cover the system symbols and be general. On a deeper level, this seems to reflect that the separation between internal and top-level rules is rather artificial and sometimes flies in the face of the core language semantics, particularly when one wants to write some general functions related to introspection, such as ValueQ
.
ValueQ
has the atttributeHoldAll
. Maybe you discovered a bug? $\endgroup$HoldAll
attribute only says what will happen to the function's arguments before they are passed to the function, but nothing about how the function itself evaluates them. A situation when function is not supposed to evaluate arguments passed to it but does that nonetheless, is called evaluation leak, and is something to watch out for when you developHoldAll
etc functions. For the case at hand, this was discovered before, perhaps more than once, e.g. here. $\endgroup$ValueQ
, as Leonid and I have been discussing. I deleted my answer until I have time to consider all this and expand/revise accordingly. $\endgroup$True
forHold[2]
or"a" + "b"
orx[1][2]
(wherex
is undefined). I am exploring another possibility now. $\endgroup$