2
$\begingroup$

I want to define a function OV which acts as shorthand notation for OptionValue, so that I can use it as in

Options @ foo = {"a" -> 1};
foo[x_, OptionsPattern[]] := x + OV @ "a"

The naive definition of OV would be

OV[args___] := OptionValue[args]

The above, however, does not work as expected:

OV[args___] := OptionValue[args];
Options @ foo = {"a" -> 1};
foo[x_, OptionsPattern[]] := x + OV @ "a";
foo[x]
(* Out = x + OptionValue[a] *)

Tracing the evaluation of the above example and comparing with the regular usage of OptionValue quickly reveals where the problem lies:

OV[args___] := OptionValue[args];

Options@foo = {"a" -> 1};
foo[x_, OptionsPattern[]] := x + OV @ "a";

Options@foo2 = {"a" -> 1};
foo2[x_, OptionsPattern[]] := x + OptionValue @ "a";

Trace @ foo[x] // Column

Trace @ foo2[x] // Column

Now, this kind of non-standard evaluation is not news (see for example this question). However, from that questions I was left wondering if there is a way to define some kind of "alias" function for OptionValue. This nonstandard "macro-like" kind of evaluation makes it not trivial to me how to do it in general.

$\endgroup$
  • $\begingroup$ You can always force that in with a With, if you're willing to do that. And then maybe define a package level macro that's like withMyHooks which hooks in lots of stuff like that implements an UpValues hook on SetDelayed so you can use it like withMyHooks@lhs:=rhs and it'll unwrap to With[{...}, lhs:=rhs]. $\endgroup$ – b3m2a1 Aug 3 '17 at 17:47
  • $\begingroup$ @b3m2a1 I may be getting what you are saying, but maybe not. It looks like an interesting solution, care to make it into an answer? :) $\endgroup$ – glS Aug 3 '17 at 17:49
  • $\begingroup$ Sure. Give me a moment. $\endgroup$ – b3m2a1 Aug 3 '17 at 17:50
4
$\begingroup$

So it took some finagling to get the replacement to be as nicely extensible as I wanted it, but this should work for you. Basically we define a set of replacements that we can change however we like ($myHooks) and a function that will inject them, using With (withMyHooks). Then stick an UpValue on that to determine if it's being used on the lhs of a function assignment, and if it is to expand to wrap on the whole assignment.

Of course, that's a long description for a little bit of code:

$myHooks =
  HoldComplete@
   {
    OV = OptionValue,
    OP = OptionsPattern
    (*other hooks*)
    };

withMyHooks // ClearAll;
withMyHooks[e_] :=
  Replace[$myHooks,
   HoldComplete[l_] :>
    With[l, e]
   ];
withMyHooks~SetAttributes~HoldAllComplete;
withMyHooks /: 
 HoldPattern[SetDelayed[withMyHooks[lhs : _[___]], rhs_]] :=

 withMyHooks[lhs := rhs]

Then we'll use this on a function:

myF // Clear
withMyHooks@
  myF[OP[Plot]] :=
 OV[PlotRange]

myF[]

{Full, Automatic}

myF[PlotRange -> 1]

1

And it works as we want it to, because it just turned OV and OP into shortened forms.

| improve this answer | |
$\endgroup$
  • $\begingroup$ very interesting, and may be useful in some circumstances. However having to wrap all expressions with withMyHooks often kind of defeats the purpose of shortening the function name. Is there now way to avoid having to manually wrap all expressions in which we want to use the shorthand notation? $\endgroup$ – glS Aug 4 '17 at 10:49
1
$\begingroup$

In your program you can replace "a" by a to make it more general. As Mathematica is symbolic, you don't need to quote symbols.

Replacing x by OptionValue@x or even OV@x in a complex expression may make it unreadable.

I suspect you would actually like to get rid completely of OptionValue. Do this:

Options@foo = {a -> 1};
foo[x_, options___Rule] := x + a /. {options} /. Options@foo 

It was the standard way of handling options before Mathematica v6.

| improve this answer | |
$\endgroup$
  • $\begingroup$ how does replacing "a" with a make it more general? It would argue it makes the code more prone to error, as for example if a has a DownValue before Options@foo={a->1} is used the options will not be set correctly $\endgroup$ – glS Mar 21 at 18:11
  • $\begingroup$ The symbol acan represent anything, including, in particular "a". The converse is not true. Consequently, indeed, you have to keep you symbols clean or protect them. $\endgroup$ – Pierre ALBARÈDE Mar 21 at 22:26
  • $\begingroup$ This is how to protect: foo[x_, options___Rule] := ReleaseHold[Hold[x + a] /. {options} /. Options@foo]. Quote is poor man's Hold but finally it is good too. However, it obliges you to write "a" which looks strange in formulae. So if you want naked a you will follow me. $\endgroup$ – Pierre ALBARÈDE Mar 21 at 22:35
  • $\begingroup$ The procedure is equivalent to With that was pointed out above. Usually I begin with With[{a=_},f[a]] and if I need I turn on the option system . The With for "private" parameter, the option system for "public" parameter. Finally option is a way to publish attributes of the expression. $\endgroup$ – Pierre ALBARÈDE Mar 22 at 10:53
  • $\begingroup$ the fact that "a can represent anything" does not mean that the expressions using a and "a" are equivalent. I personally find it much easier to use strings rather than having to protect all the symbols used in the options. $\endgroup$ – glS Mar 22 at 10:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.