TransWikia.com

When is a symbol a Symbol? Is there an easy Mathematica way to test if an object is a symbol sort of like a SymbolQ?

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}

4 Answers

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

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP