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 fromfin runtime. – jkuczm Nov 20 '17 at 12:03f? – matheorem Nov 20 '17 at 12:59Options@g := Options@f, but first call toSetOptions[g, ...]will assign an explicit list of options, so maintaining inheritance from, possibly changing,Options@fwould requireUpValuesoverridingSetOptions[g, ...]. – jkuczm Nov 20 '17 at 13:38Join" method I see exactly opposite: as reducing dependency between default values ofgandfoptions. Whethergcallsfis an implementation detail and should not be a concern for user ofg. If changing default options offchanges behavior ofg, it forces users ofgto keep track of those implementation details. – jkuczm Nov 20 '17 at 14:28Join" method, changing default option values offdoes not change behavior ofg. To change behavior ofgyou need to change options ofgwhich means that default option values ofgandfare decoupled. – jkuczm Nov 20 '17 at 14:33gcallsfto run. Iffis changed, then thefthatgcalled should automatically change, and this is what normal functions behave. However, the join approach make it abnormal, and set a trap. – matheorem Nov 21 '17 at 00:46fis so fundamental that other functions are defined in terns of it, then why are you redefining it and adding options? Ifgcan take any options thatftakes, then surelyOptions[f]should be defined beforeOptions[g]. – Jason B. Nov 21 '17 at 01:57Join" method as a flaw, but more as a feature that can be used intentionally. This does not exclude existence of situations in which "OptionsPattern" method might be more reasonable. In "OptionsPattern" method, fact thatfaccepts also options ofPlotneeds to be remembered when callingf. "Can Mathematica automatically remember this fact for us?" is, I think, very good question, and this is how I read your post. – jkuczm Nov 25 '17 at 20:59Optionsin one cell at the top, starting with symbols having no dependencies. Now place all definitions in any cell below them, this wayOptionsare easier to manage because you can see dependencies right away, resulting in no breaks in continuity. Similarly, you can do this with definitions. It has worked for me so far, or is this a bad practice? I'm really asking! – Jules Manson Nov 19 '22 at 10:01