Mathematica Programming » Code Structure
Function ⋆Values and Definition Clearing
DownValues
If OwnValues
are standard value bindings, DownValues
are your basic function bindings:
f[x_]:=x
DownValues@f
{HoldPattern[f[x_]]:>x}
This is also why we can define functions as patterns. All that happens is a series of replacement rules are tried, based on the DownValues
of the expression. Really, one can imagine that all Mathematica does to execute an expression is apply ReplaceRepeated
on the ⋆Values
.
Obviously the system is more complex, because of Attributes
and the coming discussion of evaluation order and things, and also more optimized, but it's a good concept to keep in mind.
Just like with OwnValues
we can manipulate the DownValues
via
One final thing. On the name DownValues
: one can imagine that the system is looking "down-stream" in the expression to see if the arguments match the defined replacement pattern.
UpValues
UpValues
are a highly useful, if not necessarily intuitive feature of the expression structure. While DownValues
look "down-stream", UpValues
look "up-stream". What this means in practice is they are patterns applied on an expression by the arguments of the expression which are, in a way, looking "up-stream". This is probably clearest via example, but first we need to know how UpValues
are set.
First off, there is a function UpSet
( ^=
) and it's partner UpSetDelayed
( ^:=
) which will do this
h[b[c___]]^:=b[h,c]
This has set UpValues
on b
:
UpValues@b
{HoldPattern[h[b[c___]]]:>b[h,c]}
Then let's test this:
h[b[1,3,4,2,1]]
b[h,1,3,4,2,1]
The most useful aspect of this behavior is that it requires only that we give definitions to the down-stream symbol, not the head, so we can use this to "overload" the basic functionality of a built-in function, which is more efficient and also cleaner and safer.
Moreover, this means we can apply definitions to functions which we otherwise couldn't. We'll demonstrate this with the MessageName
function. Aliased with ::
I'll also demonstrate the better way to set these definitions, using TagSet
( /:
)
AssociationInterface/:HoldPattern[
MessageName[AssociationInterface[a_Association],key_]
]:=a[key];
TagSet
only sets the definition on the symbol to the left, unlike UpSet
which sets the definition on every argument.
The HoldPattern
is necessary to make sure the expression doesn't evaluate while we set the UpValue
, which will make more sense when we get to discussions of evaluation order.
Now we can see how our definition works:
AssociationInterface[<|
"a"->b,
"b"->d,
"e"->f
|>]::a
b
MessageName
automatically converts its second argument to a String
, so the keys of our Association
need to be strings, but we can see the definition works.
One thing that should be mentioned before moving on is that the UpValues
of the arguments of a symbol are applied before the DownValues
of the enclosing head, although the DownValues
of the argument itself are applied before its UpValues
SubValues
SubValues
are the final type of values we need to deal with here. They provide an extension of DownValues
to multiple sets of arguments as follows:
s[arg_][arg2_]:=arg+arg2;
SubValues@s
{HoldPattern[s[arg_][arg2_]]:>arg+arg2}
Clear and ClearAll
With ⋆Values
discussed it's possible to understand the operations of of the functions Clear
and ClearAll
. Consider some symbol we've set a lot of definitions on:
m[1]=1;
m[2]=10;
m[5]=100;
HoldPattern[m[1][3]]=20;
m[s_String]:="soup";
HoldPattern[Print[m[x_]]]^:=x+"Print";
m:=35
OwnValues@m
DownValues@m
UpValues@m
SubValues@m
{HoldPattern[m]:>35}
{HoldPattern[m[1]]:>1,HoldPattern[m[2]]:>10,HoldPattern[m[5]]:>100,HoldPattern[m[s_String]]:>"soup"}
{HoldPattern[HoldPattern[Print[m[x_]]]]:>x+"Print"}
{HoldPattern[HoldPattern[m[1][3]]]:>20}
All Clear
does is remove the ⋆Values
:
Clear@m
OwnValues@m
DownValues@m
UpValues@m
SubValues@m
{}
{}
{}
{}
ClearAll
on the other hand can also remove Options
and Attributes
.
We see that Clear
doesn't remove these:
SetAttributes[m,Listable];
Options[m]={"why"->"me?"};
Clear@m
Options@m
Attributes@m
{"why"->"me?"}
{Listable}
But ClearAll
does:
ClearAll@m
Options@m
Attributes@m
{}
{}