# How to test whether an expression is a valid variable?

I would like to test whether an argument is a valid variable for functions like Solve and DSolve. For instance several functions generate a "dsvar" or "ivar" message on bad input:

DSolve::dsvar: 2 x cannot be used as a variable. >>
Integrate::ivar: Sin[x] is not a valid variable. >>

I would like to check an argument before passing it onto one of these functions.

I used the function withBlockedVars in this answer to come up with what seems like a maybe-not-very-bad way. I block the variable and attempt to assign a value to it.

ClearAll[withBlockedVars];
SetAttributes[withBlockedVars, HoldRest];
withBlockedVars[Hold[expr_], code_] :=
With[{heldVars =
s_Symbol /; Context[s] === "Global" && DownValues[s] === {} :> HoldComplete[s],
Infinity,
HoldComplete]},
heldVars /. HoldComplete[vars_List] :> Block[vars, code]]

SetAttributes[variableQ, HoldAll];
variableQ[x_] := withBlockedVars[Hold[x], Quiet@Check[x = 0; True, False]];


Tests:

t = 2;
variableQ[2^t]
(* False *)

t = 2; x = 3;
variableQ[x[2^t]]
(* True *)

variableQ[t[2]]
(* True *)

variableQ[Subscript[t, 2]]
(* True *)


My use-case is for defining functions, something like this:

f[eqn_Equal, var_?variableQ] := code


The code might call NDSolve or Plot and so on.

Is there a better way to check var? Perhaps there is a built-in function I missed?

[Edit: Any solution involving Pattern, PatternTest, Condition etc. would be acceptable. I'm not sure I can think of all the alternative possibilities.]

-
I thought perhaps "bulletproofing" or "package-writing" might be a good tag, but I found none similar. –  Michael E2 Jan 10 at 21:18
Are you looking for another variableQ or are you OK with alternate approaches? –  rm -rf Jan 10 at 21:26
@rm-rf I'm ok with alternate approaches. –  Michael E2 Jan 10 at 21:29
If you define a variable as an expression which can be assigned a value (is an L-value), then the test based on Block alone will miss some cases, e.g. Protected symbols. –  Leonid Shifrin Jan 10 at 21:32
What about using the built-in checks? Something like variableQ[var_] := Quiet[Check[Solve[0, var]~Quiet~Solve::naqs; True, False, Solve::ivar], Solve::ivar] –  Rojo Jan 11 at 0:59

Instead of a variableQ, how about constraining var to be a Symbol, giving your function the HoldAll attribute and then Blocking the variable? I use this idiom/pattern all the time. As an example:

Clear@f
SetAttributes[f, HoldAll]
f[expr_, var_Symbol] := Block[{var}, Solve[expr, var] /. var -> Defer@var]
f[expr_, var_[n___]] := Block[{var}, f[expr /. var[n] -> var, var] /. var -> var@n]


Now try the following:

x = 1;
f[x^2 + 2 x + 1 == 0, x]
(* {{x -> -1}, {x -> -1}} *)

a[1] = 2;
f[a[1]^2 + 2 a[1] + 1 == 0, a[1]]
(* {{a[1] -> -1}, {a[1] -> -1}} *)


No ivar error. This also takes care of cases like x^2 or 2 x, because these are not Symbols.

-
You don't have to use Block - if the symbol evaluates, it is not a valid variable (does not match _Symbol any more) :). Otherwise, that's what I'd also do - +1. –  Leonid Shifrin Jan 10 at 21:35
@LeonidShifrin The Block takes care of cases where there might be stray assignments in Global that you don't want evaluating inside the body. (for instance, in my example above, removing the Block will give you an ivar error) Given Michael's use case, I suspect this would be a desirable option. –  rm -rf Jan 10 at 21:37
I see. As an alternative, you could simply use HoldFirst, and omit the Block. But I agree, you construct is generally more useful, and this is also what I usually do. –  Leonid Shifrin Jan 10 at 21:42
This was the first thing I thought of, too, mainly for simplicity (for myself). But I'm writing it for students and I anticipate their wanting to do things their own way and having "stray assigments" about. We also will have systems and I wondered if it would be hard to handle x[1] etc. If I do it this way, I could write my own error message. +1 –  Michael E2 Jan 10 at 21:44
@MichaelE2 x[1] can be handled with a second definition: f[expr_, var_[n___]] := Block[{var}, f[expr /. var[n] -> var, var] /. var -> var@n]`. –  rm -rf Jan 10 at 21:57