In addition to the differences between these global rules reflected in the patterns for which assignment operators put the global rule into one or another
...Value, there is another, and IMO,no less important difference, and that is in how these rules are used in the evaluation sequence.
OwnValues represent symbols themselves, and therefore they are applied when symbols are evaluated. If these symbols are atomic elements inside some expression, and evaluation comes to them, they are (normally) replaced with the r.h.s. of their
OwnValues. The more interesting situation is when the symbols are heads of some normal expressions. In this case, since heads are evaluated very early in the evaluation sequence, these symbols are also replaced with what their
OwnValues instruct, but now as heads.
This has several consequences. Here is one example:
As you can see, none of the Attributes assigned to
f actually had a chance to play, since
f was replaced by
h before the attributes were even considered by the evaluator. The same will happen when we add some definitions (as
Once again, these definitions had no chance to execute.
These cases may seem contrived, and indeed they do not happen very often as results of a deliberate coding, but they can quite often happen due to a mistake, and then are hard to debug. One very relevant discussion is here. To complete this part, let me mention another very insidious case. Let us just take the last example and reverse the order in which we were creating definitions:
Somewhat miraculously, the code worked, seemingly contradicting my previous statements. But let us look closer:
is all right, but this
may be a surprise. This is an example of evaluation during assigments. A more complete discussion can be found e.g. here. This is just another pitfall to watch out for.
Evaluation: DownValues vs SubValues
It is useful to know that
DownValues are applied before
SubValues. Therefore, here we get:
Had I reversed the order of definitions, and we'd have got this:
SetDelayed::write: Tag Integer in 1[x_] is Protected. >>
This is another case of evaluation during an assigment: the first definition for
f evaluated inside
SetDelayed when we attempted to create the second one.
Another important difference: functions defined via
DownValues can hold all their arguments if needed, through the
HoldAllComplete attributes. Functions defined with
SubValues, however, can only normally hold the "innermost" group of arguments:
During evaluation of In:= 2
The only workaround I am aware of, when such construct is needed, is this:
Similar situation holds for some other attributes, e.g.
Listable. This topic is discussed in more detail e.g. here
DownValues vs UpValues: System-wide changes vs. locality
There are several important ways in which
UpValues are different from
DownValues. One very important aspect is that they allow you to "softly" overload some system functions only on certain symbols. The importance of this 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.
DownValues vs UpValues: Order of evaluation
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
g are applied before
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
g will be applied before
g - if
g[args] can evaluate inside
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:
f[x_] := x^2;
g /: f[g[x_]] := Sin[g[x]];
g[x_] := Cos[x];
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
HoldFirst, or if we wrap
Unevaluated - in both cases we give the evaluator the instruction to skip the evaluation of
DownValues vs UpValues: Hold-attributes
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:
During evaluation of In:= Evaluated
If one wants to absolutely prevent the search for
UpValues, one should give a function the
HoldAllComplete attribute. For example:
I tried to outline and illustrate several technical points which pop up quite frequently in practice and can be frustrating at first. My main message is that a huge difference between different kinds of rules is induced by the way they are used in the main evaluation sequence. This difference may not be immediately apparent, but it affects all aspects of how these rules are used in practice.