1
$\begingroup$

Consider the following code running on a fresh Mathematica session with no symbols defined:

p[s_String] := Protect[s];
p["x"]

This doesn't do anything because there is no symbol named x yet, so no symbols match the string pattern "x". No problem, we can resolve this by using Symbol:

p[s_String] := Protect[Symbol[s]];
p["x"]

This doesn't work either, since Protect has the attribute HoldAll. So we can use Evaluate:

p[s_String] := Protect[Evaluate[Symbol[s]]];
p["x"]

Great! Now it works. Except... If the symbol we want to protect does already exist, then it won't work:

y = 1;
p[s_String] := Protect[Evaluate[Symbol[s]]];
p["y"]

This will execute Protect[1] because Symbol["y"] evaluates to 1. How can one write a function p which protects a symbol, given via a string, whether or not it already exists?

Thanks.

EDIT: A solution was given in the comments:

p[s_String] := Protect @@ ToExpression[s, StandardForm, Hold];

However, now I have another similar issue. I would like to find out if s is a proper symbol, so that e.g. p["1"] or p["a b"] will return an error message. So I tried the same trick with Head:

p[s_String] := 
  If[Head @@ ToExpression[s, StandardForm, Hold] === Symbol, 
   Protect @@ ToExpression[s, StandardForm, Hold], 
   Print[s, " is not a symbol!"]];

This works only if the symbol was not defined previously. For example, if I type x = 1 and then p["x"] I will get x is not a symbol because its Head is Integer. So again, it's the same problem as above: How can I look at the symbol itself without it being replaced by its value?

$\endgroup$
10
  • $\begingroup$ What if you just did p[s_String] := If[MemberQ[Names[s], s], Protect[s]] $\endgroup$
    – flinty
    Aug 6 '21 at 15:17
  • $\begingroup$ Protect @@ ToExpression[s, StandardForm, Hold]? $\endgroup$
    – Michael E2
    Aug 6 '21 at 16:23
  • $\begingroup$ @flinty: But then it won't be protected if it doesn't already exist. I want to protect that symbol from future modifications whether or not it already exists. $\endgroup$
    – CMB
    Aug 6 '21 at 17:38
  • $\begingroup$ @MichaelE2: Thanks, I think that works! $\endgroup$
    – CMB
    Aug 6 '21 at 17:40
  • $\begingroup$ I now have a similar problem, for which this trick doesn't work. Please see my edit. Thanks! $\endgroup$
    – CMB
    Aug 6 '21 at 18:36
1
$\begingroup$

I found a simple solution to my problem:

p[s_String] := 
  Quiet[Check[Protect @@ ToExpression[s, StandardForm, Hold], 
    Print[s, " is not a symbol!"], Protect::pssl]];

This solution simply lets Protect "do the work for me" (as suggested by @MichaelE2 in the comments). In my actual code, there is much more that happens after calling Protect, but I only continue the evaluation if Protect was successful.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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