Optional positional arguments must occur at end

Actually, here’s an interesting idea. We can actually check a set of signatures for ambiguities at definition time. So we could make it an error to write a method definition that is ambiguous with itself. Currently it’s impossible to write such a method, so this would be non-breaking. That would make the f(a=1, b=2, c, d=4, e=5) = ... method definition illegal. But it would allow something like this:

F(f=identity, s::String, n::Integer=0) = ...

This definition would expand to:

F(f, s::String, n::Integer) = ...
F(f, s::String) = F(f, s, 0)
F(s::String, n::Integer) = F(identity, s, n)
F(s::String) = F(identity, s, 0)

Since that set of method signatures isn’t ambiguous, it’s fine. This might actually make it easier to avoid accidental method ambiguities since if you can write the signature with a single method then you’d get an error if it’s ambiguous.

<aside>

We used to emit a warning at method definition time for method ambiguities, back in Julias 0.1-0.3 (IIRC). But this was a huge drag because it was common when multiple packages extended the same generic function (often from Base), for method ambiguities to arise between these extensions. Something like this:

# in Base
f(a::AbstractA, b::AbstractB) = # generic logic

# in PackageX
f(a::SpecificA, b::AbstractB) = # specialization on A

# in PackageY
f(a::AbstractA, b:: SpecificB) = # specialization on B

Now you have an ambiguity if someone calls f(a::SpecificA, b::SpecificB). So loading packages commonly lead to screenfuls of ambiguity warnings, which just made everything seem janky and broken. Of course, if someone does call the ambiguous method, we don’t know what to do, so we throw an error. But it’s fairly common that this intersection method doesn’t make much sense and never gets called, in which case leaving it ambiguous is just fine. Anywho, that’s why we changed method ambiguities from definition-time warnings into runtime errors.

</aside>

17 Likes