Szabolcs showed in this post, that there are possibly two approaches to inherit function options. He recommended the Join
approach. At first, I agreed. But today I found this approach can cause problem if we don't take care.
For example, If you have a function f
with options set as
Options[f] = {...};
and you also have a bunches of function g,h,p,q,... that all inherit options from f
via Join
. That is
Options[g]=Join[...,Options[f]]
etc. Then what if you change the Options
of f
? Then you have to find all function g,h,p,q,... that inherit from f
, and reevaluate their definition. This kind of evaluation dependency of function definition is kind of not reasonable.
So I turned to OptionsPattern[]
approach. It doesn't have the above problem. But it got its own problems. The small problem is that SyntaxInformation
needs special care, and need to use undocumented OptionNames
. This one is relatively easy to tackle, here is my attempt to auto the setting process of SyntaxInformation
While the bigger problem of OptionsPattern[]
approach is due to Options
function. The Mathematica designed Options
function to be that can only show explicit default options, not those hidden in OptionsPattern[]
. For example,
ClearAll[f];
Options[f]={opt1->1};
f[x_,opts:OptionsPattern[{f,Plot}]]:=...
Evalute Options[f]
can only give you {opt1->1}
. However, the true available options of f
should be all Plot
options plus {opt1->1}
. This behaviour of Options
makes inheritance impossible. Because somewhere in another function g
that inherit f
must use FilterRules
. For example
ClearAll[g];
g[y_,opts:OptionsPattern[{g,f}]]:=f[y,FilterRules[{opts},Options[f]]
However, the above definition is wrong. Because, Options[f]
is incomplete!. What is more, due to Options
behaviour, OptionsPattern
got problems too Because according to the doc, OptionsPattern[{f}]
is equivalent to Options[f]
, so OptionsPattern[{g,f}]
is also incomplete.
So my thought is that to make OptionsPattern[]
works properly. We need two additional function trueOptions
and the corresponding trueOptionsPattern
. The trueOptions
should give all available options including those hidden in OptionsPattern
, so trueOptions[f]
equals correctly Plot
options plus {opt1->1}
. And trueOptionsPattern[f]
treated as obtained from trueOptions[f]
. So we can use the OptionsPattern
approach to inherit.
ClearAll[g];
g[y_,opts:trueOptionsPattern[{g,f}]]:=f[y,FilterRules[{opts},trueOptions[f]]
Unfortunately, Mathematica doesn't provide such functions
tureOptions
seems easier to implement, for example
ClearAll[getOptionsPatternContent];
getOptionsPatternContent::noDownValue =
"`1` got no DownValue. It probably not defined";
getOptionsPatternContent[symbol_] := Module[{},
funcForm = If[DownValues[symbol] === {},
Message[getOptionsPatternContent::noDownValue, symbol]; Abort[],
FullForm[
Cases[DownValues[symbol] /.
Verbatim[symbol][x___] :> nullHead[x], nullHead[___],
Infinity][[1]]];
optionsPatternContent =
Complement[
Flatten@Cases[funcForm, Verbatim[OptionsPattern][x_] :> x,
Infinity], {symbol}]]];
ClearAll[trueOptions];
trueOptions[symbol_] := Module[{optionsPatternContent},
optionsPatternContent = getOptionsPatternContent[symbol];
DeleteDuplicates@
Flatten@Join[
If[optionsPatternContent === {}, {},
Table[If[Head@i === Rule, i, trueOptions@i], {i,
optionsPatternContent}]], Options@symbol]]
But how to implement a trueOptionsPattern[]
? A naive try
ClearAll[trueOptionsPattern];
trueOptionsPattern[] := OptionsPattern[];
trueOptionsPattern[x_] :=
OptionsPattern[
Flatten@Table[If[Head@i === Rule, i, trueOptions@i], {i, x}]];
is not working. Because trueOptionsPattern[{g,f}]
needs g
, but g
is not defined yet.
What is more? OptionsPattern[]
seems has some subtleties with OptionValue
, so I am not sure whether this line of thought will work. Any suggestions and comments?
Options[g]
, but you can still inherit default option values fromf
in runtime. $\endgroup$f
? $\endgroup$Options@g := Options@f
, but first call toSetOptions[g, ...]
will assign an explicit list of options, so maintaining inheritance from, possibly changing,Options@f
would requireUpValues
overridingSetOptions[g, ...]
. $\endgroup$Join
" method I see exactly opposite: as reducing dependency between default values ofg
andf
options. Whetherg
callsf
is an implementation detail and should not be a concern for user ofg
. If changing default options off
changes behavior ofg
, it forces users ofg
to keep track of those implementation details. $\endgroup$Join
" method, changing default option values off
does not change behavior ofg
. To change behavior ofg
you need to change options ofg
which means that default option values ofg
andf
are decoupled. $\endgroup$