41
$\begingroup$

The system function Remove evidently exists primarily to turn a fatal problem into an annoying one, by giving the user a (rather blunt) instrument with which to resolve symbol clashes. Presumably, it works by removing the entry for a given symbol from whatever internal table Mathematica uses to look them up by name. However, there can be some interesting consequences when you have a reference to a symbol that you later Remove. First, we need to set up a symbol that we will remove, and give another symbol a reference to it:

In[1]:= y = 3
Out[1]= 3

In[2]:= x := y

In[3]:= x
Out[3]= 3

In[4]:= OwnValues[x]
Out[4]= {HoldPattern[x] :> y}

And then we can remove it:

In[5]:= Remove[y]

In[6]:= x
Out[6]= Removed["y"]

Now, we have Removed["y"], which is the ghost of the symbol we just bludgeoned to death. However, contrary to appearances, Removed is not the head of the expression:

In[7]:= x // Head
Out[7]= Symbol 

It's still a symbol. It appears to just have had all its values cleared when it was Remove'd. However, you need an elaborate incantation to contact a symbol that's moved on into the spirit world in order to verify this:

In[8]:= OwnValues[x]
Out[8]= {HoldPattern[x] :> Removed["y"]}

In[9]:= ReleaseHold[Hold[OwnValues[x]] /. OwnValues[x]]
Out[9]= {}

That's OK, though, because I can still give new values to the removed symbol!

In[10]:= With[{yy = x},
          yy = 17]
Out[10]= 17

In[11]:= ReleaseHold[Hold[OwnValues[x]] /. OwnValues[x]]
Out[11]= {HoldPattern[Removed[y]] :> 17}

So, what can't you do with a removed symbol that you can do with an ordinary symbol? And is there any reliable programmatic way to check whether a symbol really has been removed, seeing that it has a perfectly ordinary Symbol head? I suppose you can hack something together with StringMatchQ, but that just feels wrong and it's tough to get the evaluation order right if the removed symbol might have OwnValues.

$\endgroup$
2
  • 2
    $\begingroup$ It gets even more interesting with more than one removal. An example: y=3;x:=y;x2:=y;x==x2 gives True. So far nothing unexpected; after all, both x and x2 evaluate to Removed[y]. However now write x3=y;Remove[y];x==x3 and you'll get Removed[y] == Removed[y]. Also SameQ[x,x2] gives True while SameQ[x,x3] gives False. That is, Mathematica still tracks the fact whether two Removed symbols are the same removed symbol or not although even the FullForm is the same for both removed symbols. On the other hand, Order[x,x3] gives 0. $\endgroup$
    – celtschk
    Commented Apr 30, 2012 at 14:28
  • $\begingroup$ Oops, I just notice that I omitted something crucial in my previous comment, without which it makes no sense at all! The first code fragment in the example should of course read: y=3;x:=y;x2:=y;Remove[y];x==x2 — note the addition of Remove[y]. $\endgroup$
    – celtschk
    Commented Apr 30, 2012 at 14:36

1 Answer 1

39
$\begingroup$

Some observations on Removed

Removed is not a normal head, but rather a print form. Consider a definition

x := y

Once we Remove the y, we invalidate x in a subtle but permanent way - reintroducing the y into the session won't help. Remove is really a rather special-purpose destructuve operation, aimed more at removing auto-generated symbols. In a system of inter-connected functions (possibly from different packages), for this reason Remove is safe only if nothing depends on the symbol being removed. Resolving shadowing is the only mainstream (frequent, non-advanced) application of Remove I am aware of.

I learned from the book of David Wagner ("Power programming with Mathematica",page 251), that while the removed symbol is printed (and has a FullForm) as Removed["sym"], applying Head to it yields Symbol. The reason for that is that Removed["sym"] still represents a symbol, albeit marked for removal. For code like this:

Clear[b];b := a;Remove[a];b

you can not, e.g., "resurrect" a in b with this:

b /. Removed[x_] :> ToExpression[x]

Removed["a"]

but, since Removed["a"] represents a symbol marked for removal, this will work, to at least reconstruct the value of b:

b /. Cases[b, s_Symbol :> (s :> a), {0, Infinity}, Heads -> True]

a

You can also analyze which symbols in some expression were Remove-d, by something like this:

Clear[removedNames];
removedNames[expr_] :=
  Flatten[
    StringCases[
      ToString /@ Union@Cases[expr, _Symbol, Infinity, Heads -> True],
      ShortestMatch["Removed[" ~~ x__ ~~ "]"] :> x]
  ]

So that

removedNames[OwnValues[y]]

{"x"}

Summary

So, to summarize:

  • Removed is not a normal head but a display form.
  • When you Remove a symbol, Removed[sym] still represents this symbol, but being marked for removal. My understanding is that this is still a reference to the symbol table, but it becomes opaque and not bound to the symbol name any more. However, apparently, Mathematica still allows you to manipulate it, pretty much as if you manipulated the normal symbol. In particular, you can, for the purposes of pattern-matching, still count on it being a Symbol. This allows us to do some things as if the symbol in question has not been removed, particularly by using local rule substitutions.
  • As you demonstrated, you can still change some ...Values for Remove-d symbols with local rules. While it looks like you can reconstruct many of the symbol's properties (except explicit symbol name), I would however limit such uses of these symbols to extreme cases when you need to make some definitions valid in a given Mathematica session.

EDIT: one constructive use - catching bugs induced by Remove

Here is one possibly constructive use of the above behavior. Remove-ing symbols is dangerous because it subtly invalidates definitions for other symbols which were referencing the symbols being removed. We may wish to know when this happens, but often the effects are silent and insidious. Here is a function which enables one to trigger such events:

ClearAll[remove];
SetAttributes[remove, HoldAll];
remove::remvd = "Stymbol `1` was removed!";
remove[s_Symbol, failCode_: Throw[$Failed, remove]] :=
  Module[{defs},
    With[{strsym = ToString[HoldForm[s]]},
       With[{body := (
          Message[remove::remvd , Style[strsym, Red]];
          failCode
           )},
        defs :=
          Module[{},
            OwnValues[s] = HoldPattern[s] :> body;
            UpValues[s] = HoldPattern[_[___, s, ___]] :> body;
          ]]];
    Remove[s];
    defs;]; 

What it does is to assign certain definitions to symbols already after they have been removed, using the behavior discussed above. These definitions execute arbitrary user-defined code when evaluation comes to the Remove-d symbol, in most cases (except when the symbols are inside HoldAllComplete heads). To augment it, here is a dynamic environment, in which Remove will behave that way:

ClearAll[withCustomRemove];
SetAttributes[withCustomRemove, HoldAll];
withCustomRemove[code_,failCode_: Throw[$Failed, remove]] :=
  Module[{inRemove},
    Internal`InheritedBlock[{Remove },
     Unprotect[Remove];
     Remove[arg_] /; ! TrueQ[inRemove] :=
         Block[{inRemove = True}, remove[arg,failCode]];
     Protect[Remove];
     code]];

and here is an example of use. First, the usual behavior:

ClearAll[f, a];
f[x_] := x + a;
g[] := Hold[a];
Remove[a];
{f[1], g[]}
{1+Removed[a],Hold[Removed[a]]}

Now, with our functions:

withCustomRemove[
   ClearAll[f, a];
   f[x_] := x + a;
   g[] := Hold[a];
   Remove[a];
]
{f[1], g[]}
  During evaluation of In[78]:= remove::remvd: Stymbol a was removed!

  During evaluation of In[78]:= Throw::nocatch: Uncaught Throw[$Failed,remove]
           returned to top level. >>

  Hold[Throw[$Failed,remove]]

One can specify a different behavior to be triggered on such an event. One can also generalize to Remove with several arguments. The idea is that you can take the code which is suspicious, and execute it inside withCustomRemove - which may often enable you to catch bugs related to attempts to use Remove-d symbols.

$\endgroup$
12
  • $\begingroup$ Thinking about it using all the information above, the probable implementation is that each symbol is a data structure containing its name, associated values, and a link to the context it belongs to (and the context of course needs a link to the symbol). Remove then clears the definitions and removes the symbol from its context (thus clearing the context backlink), but doesn't actually remove the symbol. When printing a symbol, the formatting routine looks at the context to decide whether it has to be printed with the symbol's name, and if there is no context, returns Removed["name"]. $\endgroup$
    – celtschk
    Commented Apr 30, 2012 at 15:11
  • $\begingroup$ @celtschk Yes, I would think so. I've edited my answer in the meantime and added something similar to what you are saying. My understanding is that the symbol remains in the symbol table, but its reference (handle) is no longer tied to a given symbol (in some context), so it can not be retrieved by the symbol lookup mechanism (which is based on symbol names, including their contexts). But, it can still be manipulated, although we lose the convenience of named symbols. $\endgroup$ Commented Apr 30, 2012 at 15:16
  • 3
    $\begingroup$ As a side note, one of the most amazing things (to me anyway) is that for most of the core language features, including subtleties like this one, we still can get insights and accurate information in accounts 15-20 years old, and not only that, but those serve us very often better than anything that came after. This shows once again that the core language did not change much, which is quite impressive, given that most recent developments in Mathematica were heavily relying on the core language. $\endgroup$ Commented Apr 30, 2012 at 19:34
  • $\begingroup$ Thinking about the question, and your answer, it seems that a Removed symbol is almost identical to a gensym in Common Lisp (or Emacs Lisp), though their purposes for existing are almost totally different. In both Mathematica and Lisp, symbols are complex, flexible data structures, not merely names that have been interned into some global table. $\endgroup$
    – Pillsy
    Commented May 1, 2012 at 16:25
  • $\begingroup$ @Pillsy I hope to understand this comment of yours better one day. Lisp is on my wish list. As far as I understand, gensyms are similar to Unique - generated symbols in Mathematica, and are used often to help with scoping issues. $\endgroup$ Commented May 1, 2012 at 16:37

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.