Mathematica Asked on September 28, 2021
Yes I know there is no built-in native function called SymbolQ
(but JavaScript does). However, could one be simulated to work for most cases? I often rely on objectName[symbol]
and makeRuleRow[symbol]
to return the name of a defined variable and its value in a ready-to-use row for structured Grid
layouts of results to computations. However, sometimes an error is returned if a variable is not a Symbol
which leaves me asking, "When is a symbol a Symbol
?"
I would like to catch such errors and return as much useful information as possible. That is why I ask if there is an easy hack for determining if a variable is a symbol.
Here is some working code where I might use such a function…
SetAttributes[symbolQ, HoldAllComplete];
symbolQ[x_] := ResourceFunction["SymbolQ"][x];
SetAttributes[{objectName}, HoldFirst];
objectName = Function[Null, SymbolName[Unevaluated[#]], {HoldFirst}];
objectName::usage =
"objectName@# returns Unevaluated shortened SymbolName.";
SetAttributes[{makeRuleRow}, HoldFirst];
makeRuleRow[symbol_, altname_ : Null, desc_ : Null] :=
Module[{name = "", prepend = ""},
If[ResourceFunction["SymbolQ"][symbol] === False &&
altname === Null,
Return[Row[{Style["Argument ", Red], symbol,
Style[" is not a symbol. Use altname in makeRuleRow.", Red]}]
], False
];
name = If[StringQ[altname], altname, objectName[symbol]];
prepend = If[StringQ[desc], desc <> " ", ""];
{Row[{Style[prepend, Brown], name, rule}],
TraditionalForm[symbol]}
]
The following is how it would be used for most cases (including an error) expected to be encountered when setting up name-value pairs for Grid
row elements…
xxx = 123;
makeRuleRow[xxx] (* this outputs name and value *)
makeRuleRow[xxx, "alternate name"] (* this creates alternate name *)
makeRuleRow[xxx, "alternate name", "this is a symbol"] (* this prepends a description and creates alternate name *)
makeRuleRow[69] (* this generates an error message suggesting a fix *)
makeRuleRow[69, "XXX"] (* bypasses error by creating alternate name *)
makeRuleRow[69, "XXX", "not a symbol"] (* bypasses error by creating alternate name and prepend a description *)
The actual output when done correctly conveniently makes {name ->, value}
rows ready to be inserted into two-column Grid layouts…
{xxx -> ,123}
{alternate name -> ,123}
{this is a symbol alternate name -> ,123}
Argument 69 is not a symbol. Use altname in makeRuleRow.
{XXX -> ,69}
{not a symbol XXX -> ,69}
I'd probably use x_Symbol
in a function argument to control evaluation. Otherwise, one might do the following (thanks to @Leonid for pointing out an oversight).
If the argument is to be evaluated before testing:
SymbolQ = MatchQ[#, t_Symbol /; AtomQ[t]] &
If the argument is not to be evaluated:
SymbolQ = Function[s,
MatchQ[Unevaluated@s, t_Symbol /; AtomQ[Unevaluated@t]],
HoldAllComplete];
Examples with the second definition:
SymbolQ@Plot
(* True *)
x = 1;
SymbolQ[x]
(* True *)
Clear[y];
SymbolQ@y[1]
(* False *)
Addendum
Here's what I had in mind for makeRuleRow
:
ClearAll[makeRuleRow];
SetAttributes[makeRuleRow, HoldFirst];
makeRuleRow[symbol_Symbol, altname_ : "", desc_ : ""] :=
"execute body of function";
makeRuleRow[symbol_, altname_ : "", desc_ : ""] :=
Null /; (Message[makeRuleRow::sym, symbol, 1]; False);
makeRuleRow[123]
makeRuleRow::sym: Argument 123 at position 1 is expected to be a symbol.
(* makeRuleRow[123] *)
makeRuleRow[y]
(* "execute body of function" *)
Addendum 2
1.
You could use Replace
or Switch
to define name
in either way below:
ClearAll[makeRuleRow];
SetAttributes[{makeRuleRow}, HoldFirst];
makeRuleRow[symbol_, altname_ : Null, desc_ : Null] :=
Module[{name = "", prepend = ""},
name = Replace[Unevaluated@symbol, {
s_Symbol :> objectName[symbol]
, s_ /; StringQ@altname :> altname
, _ -> $Failed}
];
prepend = If[StringQ[desc], desc <> " ", ""];
{Row[{Style[prepend, desccolor], name, rule}],
TraditionalForm[symbol]} /; FreeQ[name, $Failed]];
makeRuleRow[symbol_, altname_ : Null, desc_ : Null] := Null /; (
Message[makeRuleRow::args, makeRuleRow]; False);
2. Or:
ClearAll[makeRuleRow];
SetAttributes[{makeRuleRow}, HoldFirst];
makeRuleRow[symbol_, altname_ : Null, desc_ : Null] :=
Module[{name = "", prepend = ""},
Switch[Unevaluated@symbol
, s_Symbol, name = objectName[symbol]
, s_ /; StringQ@altname, name = altname
, _, name = $Failed
];
prepend = If[StringQ[desc], desc <> " ", ""];
{Row[{Style[prepend, desccolor], name, rule}],
TraditionalForm[symbol]} /; FreeQ[name, $Failed]];
makeRuleRow[symbol_, altname_ : Null, desc_ : Null] := Null /; (
Message[makeRuleRow::args, makeRuleRow]; False);
Some may prefer Switch
because they know it from another language or just find it easier to read. Too many commas for me, and I find the Replace
method easier.
3.
There are a few ways to handle complicated argument checking. Another is to call an "internal" version which throws $Failed
when there's is an error:
func[symbol_, altname_ : Null, desc_ : Null] := Module[{res},
res = Catch[iFunc[symbol, altname, desc], func];
res /; FreeQ[res, $Failed]
];
iFunc[symbol_, altname_, desc_] := Module[{ ...},
If[error1,
Message[func::err1, ...];
Throw[$Failed, func]
];
If[error2,
Message[func::err2, ...];
Throw[$Failed, func]
];
...
res (* return result *)
];
4.
Yet another way is to have the outer function process the arguments and call the internal function with canonicalized arguments (for example, iMakeRuleRow[name_, desc_]
) or indicate an error. The internal function then can assume the arguments are valid.
Correct answer by Michael E2 on September 28, 2021
I found my answer thanks to Sjoerd Smit who referenced me to the Mathematica Function Repository. And yes it is appropriately called SymbolQ
which is used like the following...
xxx = 123
ResourceFunction["SymbolQ"][xxx] (* returns True *)
A little bit ugly and long but it works. But why not fix if it isn't broken? And that is what I tried to do...
SetAttributes[symbolQ, HoldAllComplete];
symbolQ = ResourceFunction["SymbolQ"][#] &;
symbolQ[xxx] (* returns False *)
However Sjord came up with a solution that looks eerily similar to mine which leaves me scratching my head, why doesn't my alias work?...
SetAttributes[symbolQ, HoldAllComplete];
symbolQ[x_] := ResourceFunction["SymbolQ"][x];
symbolQ[xxx] (* returns True *)
Answered by Jules Manson on September 28, 2021
How about:
SymbolQ[_Symbol] = True
SymbolQ[_] = False
?
Answered by John Doty on September 28, 2021
I want to add something to the discussion about the ResourceFunction
SymbolQ. The OP observed that doing something like:
x = 1;
symbolQ = ResourceFunction["SymbolQ"];
ResourceFunction["SymbolQ"][x]
symbolQ[x]
(* True *)
(* False *)
does not work because the attributes of the resource function are not applied correctly. However, I just discovered that you can do the following instead:
x = 1;
symbolQ = ResourceFunction["SymbolQ", "Function"];
ResourceFunction["SymbolQ"][x]
symbolQ[x]
(* True *)
(* True *)
It seems like ResourceFunction["SymbolQ", "Function"]
will give you direct access to the function without having to go through the ResourceFunction
wrapper. This is also nice because it avoids some evaluation overhead from ResourceFunction
.
Answered by Sjoerd Smit on September 28, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP