4
$\begingroup$

I am trying to improve my own dsolver API. Currently it takes optional arguments. Is it possible to specify that an optional argument should say be String only? Or List Only? Or Integer only? and so on.

Just like one does for normal arguments by using _Head

An example will make this more clear. Here is just an example, where the dsolve takes 2 optional arguments. The first is "ic" and the second is "hint". I want the value of the "hint" to be String.

Of course I can always check for this when I read the optional argument. But it will be better if Mathematica did that.

ClearAll[dsolve, y, x];
Options[dsolve] = {"ic" -> {}, "hint" -> ""};

dsolve[ode_Equal, y_[x_], x_, opts : OptionsPattern[{dsolve}]] := Module[{ODE, ic, hint},
  ic = OptionValue["ic"];
  hint = OptionValue["hint"];
  ODE = <|"ode" -> ode, "y" -> y, "x" -> x, "ic" -> ic, "hint" -> hint|>;
  ODE 
  ]

Now I can call it using

dsolve[y''[x] + y[x] == 1, y[x], x, "hint" -> "linear"]

Mathematica graphics

But I am also able to do this

dsolve[y''[x] + y[x] == 1, y[x], x, "hint" -> 3]

Mathematica graphics

To fix this, I change the function to the following

dsolve[ode_Equal, y_[x_], x_, opts : OptionsPattern[{dsolve}]] := Module[{ODE, ic, hint},
  ic = OptionValue["ic"];
  hint = OptionValue["hint"];
  If[Not[StringQ[hint]], Abort[]]; (*Added explicit check*)
  ODE = <|"ode" -> ode, "y" -> y, "x" -> x, "ic" -> ic, "hint" -> hint|>;
  ODE 
  ]

and now

dsolve[y''[x] + y[x] == 1, y[x], x, "hint" -> 3]

Mathematica graphics

Is there something like the following for making the optional arguments (I am making this up ofcourse, as this is wrong syntax, but just to give the idea of what I am looking for)

Options[dsolve] = {"ic"_List -> {}, "hint"_String -> ""};

This way I do not have to add explicit check, because the call dsolve[y''[x] + y[x] == 1, y[x], x, "hint" -> 3] will not even match in the first place and will not go through.

Any suggestion if something like this is possible?

$\endgroup$

1 Answer 1

2
$\begingroup$

I am not sure how to get this working when using OptionsPattern explicitly but at the cost of a bit more syntax when calling OptionValue and in the function declaration, I got something to work with a named optional argument of variable length opts (which stores the option(s)):

ClearAll[dsolve, y, x];
Options[dsolve] = {"ic" -> {}, "hint" -> ""};
OptionChecks[dsolve] = <|"ic" -> ListQ, "hint" -> StringQ|>;

dsolve[ode_Equal, y_[x_], x_, opts___] := Module[{ODE, ic, hint},
   ic = OptionValue[dsolve, {opts}, "ic"];
   hint = OptionValue[dsolve, {opts}, "hint"];
   ODE = <|"ode" -> ode, "y" -> y, "x" -> x, "ic" -> ic, "hint" -> hint|>;
   ODE
] /; CheckOptions[dsolve, opts]

where CheckOptions is defined as

ClearAll[CheckOptions]
CheckOptions[f_] := And@@(OptionChecks[f][#[[1]]][#[[2]]]&/@Options[f]/.Missing[_, _][_] -> Nothing)
CheckOptions[f_, o__] := And@@(OptionChecks[f][#[[1]]][#[[2]]]&/@{o}/.Missing[_, _][_] -> Nothing)

The association OptionChecks[dsolve] stores a set of checks for the arguments of dsolve (it is not necessary to include checks for all arguments). dsolve only tries to evaluate when called with the sequence of options ops if and only if CheckOptions[dsolve, opts] evaluates to True.

Calling dsolve with a valid set of options evaluates while an invalid set does not e.g.:

test1

Changing the default options to invalid ones prevents evaluation with those options as intended and allows for evaluation only when specifying individual valid options e.g.:

test2

I must admit that this solution with CheckOptions is more a workaround than a solution to the explicit question asked here since the present implementation requires a named argument for the options opts and the explicit check /; CheckOptions[dsolve, opts] in the function declaration. I would be very interested in a better solution closer to the one envisioned in the original question.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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