25
$\begingroup$

Mathematica has a built-in function ValueQ. Quoting the docs,

ValueQ[expr] gives True if a value has been defined for expr, and gives False otherwise.

...

ValueQ gives False only if expr would not change if it were to be entered as Mathematica input.

This works nicely with symbols that only have OwnValues:

x = Print["boo!"]
ValueQ[x]

(* ==> True *)

It doesn't work so nicely with functions, though:

f[x_] := Print[x]
ValueQ[f[1]]

(*
     1
 ==> True
*)

This gives True, but also evaluates f[1] completely, printing 1. This is clearly dangerous, and it is important to be aware of this. Unfortunately, the docs make no mention of this under the "Possible Issues" section!

Why this happens is clear if we look at the implementation of ValueQ for non-symbols:

ValueQ[expr_] := ! Hold[Evaluate[expr]] === Hold[expr]

Question: Is it possible to implement a more robust version of ValueQ, which does not evaluate its argument in an unexpected an possibly dangerous way?

$\endgroup$
5
  • $\begingroup$ This is especially weird since ValueQ has the atttribute HoldAll. Maybe you discovered a bug? $\endgroup$
    – David
    Feb 8, 2012 at 4:39
  • 8
    $\begingroup$ @David 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 develop HoldAll etc functions. For the case at hand, this was discovered before, perhaps more than once, e.g. here. $\endgroup$ Feb 8, 2012 at 6:00
  • $\begingroup$ There are several ways to handle the meaning of 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$
    – Mr.Wizard
    Feb 8, 2012 at 9:24
  • $\begingroup$ @Leonid I am dissatisfied with the function in its current state; it's just too weird to get True for Hold[2] or "a" + "b" or x[1][2] (where x is undefined). I am exploring another possibility now. $\endgroup$
    – Mr.Wizard
    Feb 8, 2012 at 9:56
  • $\begingroup$ @Mr. Wizard I see. $\endgroup$ Feb 8, 2012 at 10:03

2 Answers 2

17
$\begingroup$

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.

$\endgroup$
5
  • $\begingroup$ This is probably obvious, but here is another case where valueQ and ValueQ differ: {valueQ@{1 + 1}, ValueQ@{1 + 1}}. Overall I think valueQ is much more useful. $\endgroup$
    – Ajasja
    Apr 23, 2013 at 14:23
  • 1
    $\begingroup$ @Ajasja Good point, thanks. Generally, picking one over another is a hard question, because there is no direct mapping between Mathematica evaluation sequence and our expectations of usefulness in this or that situation (evaluation sequence usually contains steps which we consider trivial and others we consider important, but from the system's viewpoint all steps have the same degree of importance. So, filtering out "trivial" evaluations is to a large degree a subjective matter). That said, I agree that more often than not I find ValueQ to not be appropriate for the task, unlike valueQ. $\endgroup$ Apr 23, 2013 at 14:55
  • $\begingroup$ Interestingly, under v8 and v9, valueQ /@ Unevaluated[{a, b[1], c[1][5], f[1, d, 2]}] returns {True, True, False, True} instead of four True-s. Did I miss something? $\endgroup$ Jun 11, 2013 at 8:27
  • $\begingroup$ @IstvánZachar Thanks, you spotted a typo. Should have been c[1][4]. It is unclear to me how this leaked into the answer, usually I copy the output directly from Mathematica. Will edit now. $\endgroup$ Jun 11, 2013 at 9:51
  • $\begingroup$ Ah, ok, I assumed that it was deliberate to call with a different argument than in the definition. $\endgroup$ Jun 11, 2013 at 11:54
15
$\begingroup$

Using the core of my new step function:

SetAttributes[valueQ1, HoldAll]

valueQ1[expr_] :=
 Module[{P, R = False},
   P = (P = Return[R = True, TraceScan] &) &;
   TraceScan[P, expr, TraceDepth -> 1];
   R 
 ]

SetAttributes[valueQ2, HoldAll]

valueQ2[expr_] :=
 Module[{P, R = False},
   P = (P = Return[R = True, TraceScan] &) &;
   TraceScan[P, expr];
   R 
 ]

SetAttributes[valueQ3, HoldAll]

valueQ3[expr_] :=
  Block[{Print},
    TraceScan[
      Null &, expr, _,
      If[# =!= HoldForm[#2], Return[True, TraceScan]] &
    ] // TrueQ
  ]

Long overdue update.

valueQ1 is my proposal for an alternative to ValueQ which leaks far less often. In most cases it returns the same answer that ValueQ does. It gives True if the entire expression is transformed in the course of evaluation.

valueQ2 is a trigger-happy version of Q1 that returns True if any evaluation takes place for the given expression. Leonid favors this one as being the most "pure" within Mathematica's framework, but it also makes for a lot of (arguably) false-positives.

valueQ3 is an attempt to get behavior closer to ValueQ (than Q1), and no leaks, but it may not be robust. The purpose of Blocking Print is NOT to hide the leaks (i below is used to check this), but a work-around because Print caused the method to fail; it is unknown to me what other function calls would also cause failure.

Here is a table of results for these functions compared to ValueQ:

ClearAll[a, b, c, d, f, g, gg, x];
i = 0;
a := Print["*a"];
b[1] := (i++; Print["*b"])
c[1][4] := (i++; Print["*c"])
d /: f[x_Integer, d, y_Integer] := (i++; Print["*d"])
gg[x_] := (i++; Print["*gg"]; False)
g[x_] /; gg[x] := x^2
x = 1;

Mathematica graphics

Results in red are tests that leaked.

$\endgroup$
18
  • 1
    $\begingroup$ Trace and friends have to evaluate the expression being traced, if I am not mistaken. $\endgroup$ Feb 8, 2012 at 6:17
  • $\begingroup$ No I did not yet - my comment was just based on the general considerations. I will look into it. $\endgroup$ Feb 8, 2012 at 6:29
  • $\begingroup$ Yes. For example, here: valueQAlt[{b[1]}] - it leaks evaluation. $\endgroup$ Feb 8, 2012 at 6:32
  • 1
    $\begingroup$ @Rojo Thanks. Slowly I am forming ideas about the utility of these functions. Leonid's is more like a "HasDefinitionQ" than ValueQ. My valueQ1 is similar to ValueQ but leaks less (though it still leaks). valueQ2 is really more like a "NotInertQ". The third function is experimental. I see why something like valueQ3[x = 8] leaks (while valueQ3[x = 2 + 2] doesn't), and I am not confident that it can be made to work. I think that I would have to compile a list of built-in "verbs" that specifically take action such as Set and Print and catch those before evaluation. $\endgroup$
    – Mr.Wizard
    Feb 9, 2012 at 9:11
  • 1
    $\begingroup$ I don't think there's a way to implement the built-in's behaviour without leaking in many cases. Because how could you tell if a condition's evaluation is necessary to tell if it matches or is simply the function doing it's work as in Print, Do, etc? $\endgroup$
    – Rojo
    Aug 15, 2012 at 11:14

Your Answer

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

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