5

How to get all definitions for a symbol associated with other symbols by TagSet, TagSetDelayed, UpSet or UpSetDelayed?

For example, if one has defined

area[square] ^= s^2
area[cube] ^= 6*s^2

how to obtain these definitions, not knowing the names square, cube but knowing only the name area?


I just have found that UpValues does not return definitions for MakeBoxes and N since they are stored in FormatValues and NValues correspondingly:

In[1]:= rotate /: MakeBoxes[expr_rotate, "StandardForm"] := x
UpValues[rotate]
FormatValues[rotate]

Out[2]= {}

Out[3]= {HoldPattern[MakeBoxes[expr_rotate, "StandardForm"]] :> x}

In[4]:= pi /: N[pi] = 3.14
UpValues[pi]
NValues[pi]

Out[4]= 3.14

Out[5]= {}

Out[6]= {HoldPattern[N[pi, {MachinePrecision, MachinePrecision}]] :> 
  3.14}

In this way instead of UpValues we should use a combination of UpValues, FormatValues and NValues.


When trying to output a list of FormatValues one can face problems with MakeBoxes since FormatValues gives definitions for MakeBoxes those are further processed by MakeBoxes on creating the output for the FrontEnd. This problem can be solved by switching FormatType temporarily to OutputForm or by converting these definitions to strings.

In[1]:= SetOptions[$Output,FormatType->OutputForm];
FormatValues[DialogNotebook]
Out[2]= {HoldPattern[MakeBoxes[BoxForm`apat$:HoldPattern[DialogNotebook[___]], BoxForm`fpat$_]] :> 

   BoxForm`BoxFormAutoLoad[MakeBoxes, BoxForm`apat$, BoxForm`fpat$, Typeset`CellNotebook`, 

    {{CellGroup, _}, {DocumentNotebook, _}, {PaletteNotebook, _}, {DialogNotebook, _}, {ExpressionCell, _}, {Text, _}, 

     {TextCell, _}, {Cell, HoldPattern[MakeExpression[_Cell, _]]}, {Notebook, HoldPattern[MakeExpression[_Notebook, _]]}}]}

In[1]:= ToString@FormatValues[DialogNotebook]
Out[1]= {HoldPattern[MakeBoxes[BoxForm`apat$:HoldPattern[DialogNotebook[___]], BoxForm`fpat$_]] :> BoxForm`BoxFormAutoLoad[MakeBoxes, BoxForm`apat$, BoxForm`fpat$, Typeset`CellNotebook`, {{CellGroup, _}, {DocumentNotebook, _}, {PaletteNotebook, _}, {DialogNotebook, _}, {ExpressionCell, _}, {Text, _}, {TextCell, _}, {Cell, HoldPattern[MakeExpression[_Cell, _]]}, {Notebook, HoldPattern[MakeExpression[_Notebook, _]]}}]}
Community
  • 1
  • 1
Alexey Popkov
  • 9,355
  • 4
  • 42
  • 93
  • Is your recent edit a request for an updated answer, or posted only to help others finding this question? – Mr.Wizard May 01 '11 at 07:37
  • @Mr.Wizard I think it would be better to make an updated answer with the complete solution. – Alexey Popkov May 01 '11 at 07:42
  • @Mr.Wizard The problem is really tricky. See edited part of my question. Now I understand why the developers moved all definitions for `MakeBoxes` to scarcely documented `FormatValues`. :) – Alexey Popkov May 01 '11 at 08:40
  • Nuts. Thought I had it. Out of time for now, but I'll try again later. – Mr.Wizard May 01 '11 at 08:46
  • @Mr.Wizard From the other side, there are not so many cases when Mathematica generates error messages during printing/outputting of `FormatValues`: `names = Names["*"]; If[(val = ToExpression[#, InputForm, FormatValues]) =!= {}, Quiet@Check[ToBoxes[val], Print["!!!!!", #]]] & /@ names;`. – Alexey Popkov May 01 '11 at 09:35
  • I think that generally the problem is solved. – Alexey Popkov May 01 '11 at 09:58

3 Answers3

4

Attempting to address Alexey's concerns with Howard's answer, I came up with this:

Cases[
   UpValues @@@ MakeExpression /@ Names["Global`*"],
   HoldPattern[_@_area :> _],
   {2}
]

In response to your updated requirements, here is the advanced version:

SetAttributes[otherValues, HoldFirst]

otherValues[sym_] :=
  With[{names = MakeExpression /@ Names["Global`*"]},
    Join[
      Cases[UpValues @@@ names, HoldPattern[_@_sym :> _], {2}],
      Cases[NValues @@@ names, HoldPattern[_@N[sym, ___] :> _], {2}],
      Select[Join @@ FormatValues @@@ names, ! FreeQ[#, HoldPattern@sym] &]
    ]
  ]
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • 1
    I think this solution is quite elegant! – Alexey Popkov May 01 '11 at 03:12
  • Both your and @Leonid's solutions evaluate `area`. One can avoid this by wrapping it with `HoldPattern`: `HoldPattern[area][_]`. After this the code becomes almost safe. The only exotic case when it becomes dangerous is when one define `area[surprise] ^:= Unevaluated[Evaluate[Print["Surprise! :)"]]]`. In this very special case both versions evaluate the definition. But I think we should not commonly care about such hacker's exotic. – Alexey Popkov May 01 '11 at 03:30
  • @Alexey, when would `area` have a value? If I assign `area[square] ^= s^2` and then `area = 5`, and afterward evaluate `area[square]` I get `5[square]`. – Mr.Wizard May 01 '11 at 03:34
  • @Mr.Wizard Yes, this unlikely can happen. But can: `area[square] ^= s^2; area := (Print["!"]; area =.; area)`. I just wish to have a very reliable instrument. – Alexey Popkov May 01 '11 at 03:41
  • 1
    @Alexey, using your example, if evaluate this in a fresh kernel, I do *not* get the "!" printed: ``area[square] ^= s^2; area := (Print["!"]; area =.; area) Cases[UpValues @@@ MakeExpression /@ Names["Global`*"], HoldPattern[_@_area :> _], {2}]`` – Mr.Wizard May 01 '11 at 03:48
  • 1
    @Mr.Wizard Interesting! `"!"` is printed as the result of evaluation of `area[square] ^= s^2;`, not your code! `area` is evaluated although `UpSet` has attribute `HoldFirst`: `area := (Print["!"]; area =.; area); Trace[area[square] ^:= s^2]`. But why??? – Alexey Popkov May 01 '11 at 04:12
  • If we evaluate `area := (Print["!"]; area =.; 5); area[square] ^:= s^2; UpValues[square]` we get working (!!!) definition for `5[square]` avoiding `Protected` attribute of the tag `Integer`: `5[square] = 1`! – Alexey Popkov May 01 '11 at 04:17
  • @Alexey, this behavior makes sense, or at least matches my own use of UpSet. I'll write more later when I can. – Mr.Wizard May 01 '11 at 05:18
  • @Mr.Wizard I have created a [separate question](http://stackoverflow.com/questions/5846756/) on it. – Alexey Popkov May 01 '11 at 05:53
3

You can try an exhaustive search via

Select[UpValues /@ Cases[ToExpression[Names["*"]], _Symbol], ! FreeQ[#, area] &]

which in your example will yield

{{HoldPattern[area[cube]] :> 6 s^2}, {HoldPattern[area[square]] :> s^2}}
Howard
  • 38,639
  • 9
  • 64
  • 83
  • 1
    This code is dangerous since each of the symbols in the `$ContextPath` is evaluated! There should be a way to do this without evaluation of symbols. The other problem is that this code will return also definitions having `square` on the r.h.s. or on the l.h.s but not in position of `Head` of the subexpression inside `HoldPattern`. But thank you anyway. – Alexey Popkov Apr 30 '11 at 09:06
3

The following version

Cases[
  Flatten@Map[
    ToExpression[#, InputForm, Function[sym, UpValues[sym], HoldAllComplete]] &,
    Names["Global`*"]],
  Verbatim[RuleDelayed][Verbatim[HoldPattern][_area], _]
]

will not evaluate symbols as well. It is similar in spirit to @Mr. Wizard's answer, but I prefer ToExpression to MakeExpression since the latter is tied to the FrontEnd and boxes ( at least conceptually) , while the former is a general-purpose command (although it is mentioned in the documentation that it will use rules for MakeExpression).

If you have an access to the full Mathematica session from the start, another solution would be to overload TagSet, TagSetDelayed, UpSet and UpSetDelayed so that they will record the symbol dependencies in some kind of hash. Here is an example for UpSet:

Unprotect[UpSet];
Module[{tried, upsetHash},
  upsetHash[_] = {};
  getUpsetHash[] := upsetHash;
  UpSet[f_[args___], rhs_] :=
    Block[{tried = True},
       AppendTo[upsetHash[f], 
          Select[HoldComplete[args], 
            Function[Null, Head[Unevaluated[#]] === Symbol, HoldAll]]];
       UpSet[f[args], rhs]] /; ! TrueQ[tried]
];
Protect[UpSet];

All assignments made with UpSet after this redefinition will be recorded. For example, after executing your example above, you can call

In[6]:= getUpsetHash[][area]

Out[6]= {HoldComplete[square], HoldComplete[cube]} 

This way you get your information much faster, especially if you want to make such inquiries frequently, and/or you have lots of packages loaded. You can also automate the process further, to switch to the standard definitions for assignments once you load the functionality of interest.

Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • I don't understand your objection to MakeExpression, but I have learned to listen when you speak. Will you explain further? Also, why do you need `Function[sym, UpValues[sym], HoldAllComplete]`? – Mr.Wizard Apr 30 '11 at 22:09
  • @Mr.Wizard The function is needed for wrapping the output of `ToExpression` without evaluating it. `MakeExpression` wraps its output in `HoldComplete` before evaluating it by default. BTW I think in this particular case `HoldFirst` attribute is quite sufficient. – Alexey Popkov May 01 '11 at 03:04
  • @Alexey, did you find my answer unhelpful? Anyway, I am still not seeing the need for that part of the code. From the Help: "ToExpression[input,form,h] wraps the head h around the expression produced before evaluating it." Therefore, I think `ToExpression[#, InputForm, UpValues]` should work. – Mr.Wizard May 01 '11 at 03:09
  • 1
    `ToExpression` just calls `MakeExpression` when the second argument is `StandardForm` or `TraditionalForm`: try `On[MakeExpression]; ToExpression["1+1", StandardForm]`. But for `InputForm` it does not since `MakeExpression` does not support `InputForm` as the second argument. For comparison, `ToBoxes` just always calls `MakeBoxes`. – Alexey Popkov May 01 '11 at 03:10
  • @Leonid `Function[Null, Head[Unevaluated[#]] === Symbol, HoldAll]` is new for me. And syntax coloring does not know about it too. Is it reliable form of `Function`? – Alexey Popkov May 01 '11 at 04:36
  • 1
    @Mr. Wizard This was not really an objection - I just mentioned that I prefer `ToExpression`. As for `UpValues`, you are probably right too, `HoldAllComplete` is an overkill (I just got used to this pattern in `ToExpression` for functions which don't hold their args - those would also need an `Unevaluated` inside the body) . My main point was the second part of my answer, for the first one I just displayed an alternative which seems more natural to me (but this is of course very subjective). So, in this particular case, you won't lose much by not listening to me :) – Leonid Shifrin May 01 '11 at 14:39
  • 1
    @Alexey It is an undocumented form, but I am sure you can rely on it. It is very unlikely that the support for it will be discontinued. Also, it is the only way (to my knowledge) to introduce a pure function with arbitrary number of variables which would be held (or carry other attributes). See this post of mine http://stackoverflow.com/questions/4867076/how-should-i-write-a-function-to-be-used-in-apply-in-mathematica/4867297#4867297 for an example, and at the end of it I refer to the place where this form is mentioned. – Leonid Shifrin May 01 '11 at 14:44