TransWikia.com

Refer to OptionValue externally without a specific function name

Mathematica Asked on August 9, 2021

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.

One Answer

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};
iExpandOptionValue[OptionValue[head_, opts_, __], vars___] :=
 Hold[vars] /. <|args___|> :> <|args|> /. Hold[evars___] :> (
    Unevaluated@head[opts] /. HoldPattern@head[OptionsPattern[]] :> {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} *)

Correct answer by Lukas Lang on August 9, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP