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 /:
?
Actually, g /: f[g[x_]] := h[x]
is not equivalent to f[g[x_]] := h[x]
. The latter associates the definition with f
, while TagSet (/:
) and UpSet (^=
and its delayed version, ^:=
) associate the definition with g
. This is a crucial difference and can be illustrated by a simple example. Let's say you want to have a set of variables that obey modulo 5 addition, i.e. 6 + 7 mod 5 = 3. So, we want anything with Head
mod
to behave correctly. Initially, we'd think that
a_mod + b_mod := mod@Mod[a + b, 5]
would work. But, it generates the error
SetDelayed::write : Tag Plus in a_mod + b_mod is Protected.
We could remove Unprotect
Plus
and our definition would then work, but this may cause problems with other definitions and as Plus
accumulates more definitions, it would slow down. Alternatively, we can associate the addition property with the mod
object itself via TagSet
mod /: mod[a_] + mod[b_] := mod @ Mod[a + b, 5]
or UpSetDelayed
mod[a_] + mod[b_] ^:= mod @ Mod[a + b, 5]
Setting an upvalue is somewhat more correct from a conceptual point of view since mod
is the one with the different property.
There are a couple of issues to be aware of. First, the upvalue mechanism can only scan one level deep, i.e. Plus[a_mod, b_mod]
is fine, but Exp[Plus[a_mod, b_mod]]
will throw an error. This may require you to get creative with an intermediate type. Secondly, from a coding perspective UpSetDelayed
is easier to write, but occasionally there is some ambiguity as to which Head
is the upvalue associated with. TagSet
handles that by explicitly naming the appropriate Head
, and in general, it is what I tend to prefer over UpSet
.
Some of Mathematica's operators do not have any behavior associated with them, so they're not protected. For these operators, you can define functions as you wish. For instance, I've defined
a_ \[CircleTimes] b_ := KroneckerProduct[a,b]
a_ \[CircleTimes] b_ \[CircleTimes] c__ := a \[CircleTimes] ( b \[CircleTimes] c )
and
a_ \[CirclePlus] b__ := BlockDiagonal[{a,b}]
to provide convenient shorthand notations for matrix operations that I use a lot.
My example above was a little contrived, but there are a number of times UpValues
have come in handy. For example, I found that I needed a symbolic form for the complex roots of unity that behaved appropriately under multiplication and exponentiation.
Example: A straightforward and useful example is marking a Symbol
as Real:
makeReal[a__Symbol] := (
# /: Element[#, Reals] := True;
# /: Im[#] := 0;
# /: Re[#] := #;
# /: Abs[#] := Sign[#] #;
# /: Arg[#] := Piecewise[{{0, Sign[#] >= 0}, {Pi, Sign[#] < 0}}]
) & /@ List[a]
Note the use of TagSet
as Element[ a, Reals ] ^:= True
would be ambiguous. What would the rule be attached to a
or Reals
? Also, if we wanted a positive real number, we could set Arg[#]:=0
which allows Simplify
to behave as expected, e.g. Simplify[Sqrt[a^2]] == a
.