This is a bit tangential, but challenges with default values is a common motivation for questions in this forum. My recommendation would be to avoid default values altogether (I'm specifically referring to the :
syntax for arguments, not the standard mechanism for options). They actually make your code harder to maintain than other options (which I'll discuss), because it is actually tricky to figure out what will happen when you decide to slightly change things. It can also be confusing to figure out what will happen at all with default values. There are several reasonable strategies, and of course Mathematica can implement only one set of rules, and many folks find that the choice Mathematica makes does not match their intuition. Finally, there are known "gotchas" both in terms of performance problems and flat out defects.
Let's consider a simple, motivating example (simplified version of what you started with):
myFuncB[a_ : 1, b_ : "X"] := {a, b}
This seems to give you the convenience you want:
myFuncB[]
(* {1, "X"} *)
myFuncB[9]
(* {9, "X"} *)
But then we have:
myFuncB["Z"]
(* {"Z", "X"} *)
This is perfectly reasonable, but many people are surprised. Remember, Mathematica does not have a static type system. It does not assume that a
should be an integer nor that b
should be a string, even though the default values might suggest that to a human.
We can, of course fix this to a certain degree:
myFuncC[a_Integer : 1, b_String : "X"] := {a, b}
myFuncC["Z"]
(* {1, "Z"} *)
but this won't generalize to the case where your argument patterns aren't sufficiently distinct.
A strategy that provides more flexibility as well as greater control is to use polymorphic definitions:
myFuncD[a_Integer, b_String] := {a, b};
myFuncD[a_Integer] := myFuncD[a, "X"];
myFuncD[b_String] := myFuncD[1, b];
myFuncD[b_String, a_Integer] := myFuncD[a, b];
myFuncD[] := myFuncD[1, "X"];
myFuncD[{args___}] := myFuncD[args]
I've been exhaustive in this example for illustration purposes--you typically wouldn't need every combination. But, what this gives you is akin to type checking, both in terms of cardinality of the arguments as well as individual argument "types". You could layer some Condition
expressions onto this for even more control. No matter how complicated this gets, it's still easier to maintain and understand than default values (this becomes clear quickly with just a couple of defaults).
Now, if you're in a situation where it just seems incredibly obvious that you need a default value, then you should consider either using Options
(and the related option-handling functions) or SubValues
. I won't go into how to use them or when to prefer one over the other. I bring them up just to illustrate that there are many alternatives to the explicit default-argument-value syntax.
There is a built in Default
function. I haven't used it, but it might be better than inline defaults in the argument list. I can't imagine it's better than my previous suggestions though.
Finally, if you absolutely must have a default value, limit it to just one argument and make it the last argument.