Version inconsistency with optional arguments: what if the default value doesn't match the pattern?
Update: Daniel Lichtblau authoritatively comments:
This change was intentional, and per request of the boss.
I can find no mention of this in the documentation, though I am still looking. My guess is that the old behavior was a common source of problems and someone's fix was to implement the new behavior in version 10.1.0.
On the surface at least I like the change as it shortens and simplifies code. A notable difference is that with the new short scheme the default value is not seen as a valid explicit argument:
f[All]
(* Out: f[All] -- 10.1.0 under Windows *)
I think this actually may prove useful but I can also imagine it being a new source of confusion.
Perhaps this change makes behavior more consistent than it was in the past.
Consider this behavior in older versions (here v7):
ClearAll[f, val]
f[vs_List: val] := vs
val = {1, 2, 3};
f[]
(* Output: f[] -- no match *)
ClearAll[f, val]
f[vs : (_List | HoldPattern[val]) : val] := vs
val = "foo";
f[]
(* Output: "foo" *)
I find both cases unexpected even if explainable. The new more permissive behavior makes these cases more similar as both evaluate using the current value of val
.
Breaking change
There appears to be a serious caveat to this change that I previously failed to note. In Mathematica 7 pattern Symbols are bound to expressions in the default:
f[args : {x_, y_} : {1, 2}] := {x, y}
f[]
{1, 2}
In version 10.1 this is no longer the case:
f[args : {x_, y_} : {1, 2}] := {x, y}
f[]
{}
Reference comments by Itai Seggev in
- Setting nested optional argument with a default when unpacking from a given Head
In my opinion, Optional
should just consistently not match when the default value does not match the pattern, period.
Now we have nonsense like this:
{}~MatchQ~{Optional[0., 0]}
False
You might think this is because 0~MatchQ~0. === False
.
But then why is
{}~MatchQ~{Optional[_Real, 0]}
True
when 0~MatchQ~_Real
is False
?
What is the rule here?
The situation becomes even more confusing when you start playing with HoldPattern
(for doing things like this):
G[0] := 0
Gh[0] := h@0
{}~MatchQ~{HoldPattern@Optional[_[_], G[0]]}
{}~MatchQ~{HoldPattern@Optional[_?AtomQ, G[0]]}
{}~MatchQ~{HoldPattern@Optional[0, G[0]]}
{}~MatchQ~{HoldPattern@Optional[_?AtomQ, 0]}
{}~MatchQ~{HoldPattern@Optional[h@_, Gh[0]]}
{}~MatchQ~{HoldPattern@Optional[h@_, h@0]}
True
True
False
True
False
True
I would expect to get True
for all but the first of these, which is what you get when dropping HoldPattern
.
It feels like it is not well specified what Optional
should do.