# Refer to OptionValue externally without a specific function name

I want to refer to the actual option values of a function f outside of f, using OptionValue. Since I would use the same reference in different function f, g, etc., each resolving to its own actual option values, I do not want to specify the function name f in the external OptionValue call.

OptionValue["opt"] is not resolved to OptionValue[f, "opt"] inside f. Can I circumvent this without explicitly stating OptionValue[f, "opt"] in the external assoc? BTW, that wont' work either, returning the default 1 instead of 2.

ClearAll[f];
Options[f] = {"opt" -> 1};
f[x_, OptionsPattern[]] := {
OptionValue["opt"],     (* This is evaluated correctly *)
x["Option"],            (* This is not resolved correctly *)
Evaluate[x["Option"]]   (* This is not resolved correctly *)
};

assoc = <|"Option" :> OptionValue["opt"]|>

f[assoc, "opt" -> 2]


Output:

{2, OptionValue["opt"], OptionValue["opt"]}


My expected result would be {2, 1, 1}, though I understand that the special nature of OptionValue prevents the kernel to resolve OptionValue["opt"] to OptionValue[f, "opt"] at the time it is first encountered.

• If you look at this answer, you will see that OptionValue is a magic head, which gets expanded to 3 - argument form before the r.h.s. of a function is evaluated. Which would mean that you can't accomplish directly what you are after - of course unless you use the 3-arg form of OptionValue directly. But, what is the actual need / problem? It looks like you would like to hide one option behind some other, such that both work, but only one is exposed / documented. If that is the case, I can suggest a way which I've used in such cases. Jun 16 at 9:18
• Thanks @Leonid for the explanation. I have a set of functions, like f1, f2, ..., which use roughly the same set of options, but with different values. I want to set up a global association assoc that can be used by any of the f_i, and references to options in assoc would magically be resolved to the actual option values of the f_i that uses it. The point is, that assoc is the one my clients should interact with (define), and not the f_i, so I want to make option-reference as trivial as possible, without introducing global symbols for all option values. Does it clarify my aim? Jun 16 at 10:07
• It does, but it is not an easy task. If I get some ideas, I will get back here. Jun 16 at 12:30
• @Leonid I appreciate your effort. I want to add, that whatever is used ultimately, it has to be domestic in the sense that I cannot expect the user to use anything more obscure than OptionValue to define parts of assoc (which holds more key-value pairs in reality). Moreover, I have nested options... Jun 16 at 13:59

We can (ab)use the automatic expansion of OptionValue into its three-argument-form, which works even inside rules:

Options[func] = {"opt" -> None};
{func["opt" -> 1], func[]} /. func[OptionsPattern[]] :> OptionValue["opt"]
(* {1, None} *)


Now, for your example: First, we define iExpandOptionValue that expands OptionValue into a list of expressions using the trick above.

Attributes[iExpandOptionValue] = {HoldAll};
Hold[vars] /. <|args___|> :> <|args|> /. Hold[evars___] :> (
)


Note the /. <|args___|> :> <|args|> trick. This makes sure that associations inside vars are not initialized, since otherwise the replacement doesn't work as expected.

Next, we define ExpandOptionValue. It is used as the right side of a definition, where the first argument specifies variables to "expand" using the function above, and the second argument is the expression to evaluate.

HoldPattern[lhs_ := ExpandOptionValue[{vars___}, rhs_]] ^:=
Replace[
{Hold[rhs], Quiet@Replace[Hold[vars], var_ :> Pattern[var, _], 1]},
{Hold[rhs2_], Hold[pats___]} :> (
lhs := iExpandOptionValue[OptionValue["dummy"], vars] /. {pats} :> rhs2
)
]


We use a similar trick to above to extract the function on the left side using OptionValue["dummy"], which we can then pass to iExpandOptionValue. After some more replacements, we are finally done. We can now use this on your example:

ClearAll[f];
Options[f] = {"opt" -> 1};
f[x_, OptionsPattern[]] := ExpandOptionValue[{x},
x["Option"]
];

assoc = <|"Option" :> OptionValue["opt"]|>;

f[assoc, "opt" -> 2]
(* 2 *)


To see a bit what's going on, we can look at the definition of f:

Definition@f
(* f[x$$_,OptionsPattern[]]:=iExpandOptionValue[OptionValue[dummy],x$$]/. {x_}:>x[Option]

Options[f]={opt->1} *)

• +1. I planned to try something along similar lines, but now I have no need to :) Jun 16 at 16:25
• This seems be doing what it should, thanks Lukas. Let me test it a bit more with production code, and accept if stable. Jun 18 at 7:59