To me, g /: f[g[x_]] := h[x]
is just verbose equivalent of f[g[x_]] := h[x]
. Can you raise an example that you have to use /:
?
In addition to the excellent answer by @rcollyer, I'd like to emphasize a few other important things about UpValues
.
One very important aspect is that they allow you to "softly" overload some system functions only on certain symbols. The importance of this was pointed out by @rcollyer, but can not be emphasized enough - this makes the effect of your code local, and drastically reduces the chances that your code can globally interact and affect some other part of the system or other piece of user-defined code, unlike when you Unprotect
system symbols and add some DownValues
to them.
In addition to being safe and local, such redefinitions may also be quite general, if one uses constructs like yourSymbol/:f_[_yourSymbol,rest___]:=...
. These should be used with care, but can sometimes give very concise and simple solutions. Here is one example where one code can be used to "overload" several system functions at once, giving them additional non-trivial functionality.
The next point is evaluation. The common statement you can encounter is that "UpValues
are applied before DownValues
". This must be clarified: for f[g[args]]
it means that UpValues
for g
are applied before DownValues
for f
, provided that the evaluation process already went all they way "down" to innermost parts, and then went back "up". In particular, it does not mean that UpValues
for g
will be applied before DownValues
for g
- if g[args]
can evaluate inside f
because g
has appropriate DownValues
, it will (unless f has one of the Hold
-attributes), and the presence of UpValues
won't prevent that, because (for standard evaluation), evaluation of g[args]
happens before the evaluation of f[result-of-evaluation-of g[args]]
. For example, here:
In[58]:=
ClearAll[f, g];
f[x_] := x^2;
g /: f[g[x_]] := Sin[g[x]];
g[x_] := Cos[x];
In[62]:= f[g[y]]
Out[62]= Cos[y]^2
The UpValues
for g
had no chance to apply, since g[y]
is transformed into Cos[y]
at the previous evaluation step. The situation would be different for non-standard evaluation - either if we give f
attributes HoldAll
or HoldFirst
, or if we wrap g[y]
in Unevaluated
- in both cases we give the evaluator the instruction to skip the evaluation of g[y]
:
In[63]:= f[Unevaluated[g[y]]]
Out[63]= Sin[Cos[y]]
This one is related to the previous point: one should be aware that search for UpValues
is performed even inside heads with Hold
- attributes, and therefore, UpValue
-based definitions may evaluate even when similarly-looking DownValue
- based ones won't. Example:
In[64]:= ClearAll[f,ff];
f[x_]:=Print["Evaluated"];
ff/:h_[ff[x_]]:=Print["Evaluated"];
In[67]:= Hold[f[1]]
Out[67]= Hold[f[1]]
In[68]:= Hold[ff[1]]
During evaluation of In[68]:= Evaluated
If one wants to absolutely prevent the search for UpValues
, one should give a function the HoldAllComplete
attribute. For example:
In[69]:= {HoldComplete[f[1]],HoldComplete[ff[1]]}
Out[69]= {HoldComplete[f[1]],HoldComplete[ff[1]]}
This was already mentioned by @rcollyer. This limitation was introduced for efficiency of the pattern-matcher/evaluator. I just want to stress one important and rather non-obvious consequence of it: it looks like you can not use UpValues
to overload assignment (Set
operator) so that it would work on variables assigned to objects of some specific type you introduce. Here is an attempt:
In[74]:=
ClearAll[a,myType,myCustomCode,newValue];
myType/:Set[var_myType,rhs_]:=myCustomCode;
This seems to work. But let us try:
In[79]:= a = myType[1, 2, 3];
a = newValue;
a
Out[81]= newValue
It does not do what we want, obviously. The problem is that Set
holds its l.h.s., so by the time the pattern-matching happens, it only has the symbol a
, not its value. And because we can not associate the definition with tags deeper than on the first level of the expression, the following won't work either:
ClearAll[a,myType,myCustomCode,newValue];
myType/:Set[var_,rhs_]/;MatchQ[var,_myType]:=myCustomCode;
TagSetDelayed::tagpos: Tag myType in (var_=rhs_)/;MatchQ[var,_myType]
is too deep for an assigned rule to be found. >>
To my knowledge, UpValues
can not be used to solve this problem, which is a pity, since having a usual =
syntax with custom assignment code for various data types would be convenient. For a similar discussion, see e.g. this post. This situation is not unique for Set
- the same would hold for any function that holds the argument that you want to use for your UpValue
-based definition.
UpSet
and TagSet
, UpSetDelayed
and TagSetDelayed
It is worth knowing that when you use UpSet
or UpSetDelayed
, then all tags at level 1 acquire additional definitions (rules). For example:
Clear[a,b];
Plus[a,b]^:=1;
?a
Global`a
a/:a+b:=1
?b
Global`b
b/:a+b:=1
In contrast with this, TagSet
and TagSetDelayed
are more precise:
ClearAll[a,b];
a/:Plus[a,b]:=1;
?a
Global`a
a/:a+b:=1
?b
Global`b
In my experience, the latter behavior is usually more desirable, so in most cases I prefer TagSet
or TagSetDelayed
over UpSet
or UpSetDelayed
.