3

I'd like to assign an "UpUpValue" in a way generalized to any nested head surrounding the value for which the UpUpValue would be defined. That is, if a function h[x] is called and it is nested within another two functions f[g[h[x]]], I'd like it to have a specific behavior generalizable to any head g.

I had thought this would work:

h /: f[g_[h[x_]]] := (f[x] + g[x] + h[x])

However, TagSetDelayed is limited to 2nd level specification (such that it returns that "TagSetDelayed::tagpos : "Tag h in f[g_[h[x_]]] is too deep for an assigned rule to be found."). I then tried bypassing this by defining it manually using:

UpValues[g] = {HoldPattern[f[h_[g[x]]]] :> HoldPattern[f[x] + h[x] + g[x]]}

However, it seems this does not fire successfully.

The following using UpSetDelayed also doesn't work:

f[g_[h[x_]]] ^:= (f[x] + g[x] + h[x])

As this seeks to apply the rule to specific heads only (not general g that can be used on the RHS).

Can anyone conceive of a way to accomplish this in a way that preserves generality in the head of g? For any single function g, I could simply define an UpValue or DownValue, but I would like to do so in a general way such that it is applied to any function g when it is fed the head h.

Clarification on SetDelayed:

xzczd pointed out that the following would work in principal:

f[g_[h[x_]]] := (f[x] + g[x] + h[x])

However, this associates a DownValue with the symbol f. DownValues are checked exhaustively upon calling a function, such that making many additions to DownValues of a function f that is called many times can become inefficient when compared to making UpValues (or "UpUpValues") associated with a more rarely used function h.

For example, if you wanted to define special handling for 1000 different functions sitting in h's position, this would define 1000 different DownValues of f that must be checked each time f is called, rather than one "UpUpValue" for each unique function sitting in h's spot.

xzczd
  • 65,995
  • 9
  • 163
  • 468
Ghersic
  • 1,147
  • 7
  • 21
  • 4
  • f[g_[h[x_]]] ^:= (f[x] + g[x] + h[x]) doesn't work (and it's expected). 2. Why not simply f[g_[h[x_]]] := (f[x] + g[x] + h[x])?
  • – xzczd Aug 10 '20 at 03:10
  • Depending on where you need this, you could try to build your own lightweight evaluator that applies arbitrary replacement rules to expressions – Lukas Lang Aug 10 '20 at 14:26
  • Another, (slightly overkill) approach could be to use @MrWizard's step function to do one evaluation step at a time, and then apply some additional rules each time before doing the next "normal" step – Lukas Lang Aug 10 '20 at 14:36
  • 1
    @xzczd This would certainly work. However, for efficiency reasons it is good to have an UpValue (or my aspiring UpUpValue) attached directly to the symbol h such that it does not populate the list of Downvalues for f, which would slow down very large development projects for which f might be called many, many times whereas h is only called rarely. This is an important enough point I will edit the main blog post to avoid confusion, because I too felt my request foolish for a minute after seeing your response. – Ghersic Aug 10 '20 at 20:34