Multiple dispatch based on argument given?

I have a seemingly simple question. I’m creating a struct with multiple inner constructors and I want to dispatch different methods based on the arguments given. A simple example would be:

struct OrderedPair
    x::Real
    y::Real
    OrderedPair(x::Float64,y::Float64) = new(x,y)
    OrderedPair(x::Int64,y::Int64) = new(x+1,y-1)
    OrderedPair(;x) = new(x,x+1)
    OrderedPair(;y) = new(y,y+2)
end

However, when I check the methods created their are only three:

methods(OrderedPair)
# 3 methods for type constructor:
[1] OrderedPair(; y) in Main at REPL[1]:7
[2] OrderedPair(x::Float64, y::Float64) in Main at REPL[1]:4
[3] OrderedPair(x::Int64, y::Int64) in Main at REPL[1]:5

How do I ensure the third method isn’t overwritten?

Only positional arguments, not keywords, take part in dispatch the way you’d like. You can write a “multi-level” dispatch to make it work, kind of like below:

julia> fn(; x = nothing, y = nothing) = fn(x, y)
fn (generic function with 1 method)

julia> fn(x::Float64, ::Nothing) = (x, x + 1)
fn (generic function with 2 methods)

julia> fn(::Nothing, y::Float64) = (y, y + 2)
fn (generic function with 3 methods)

julia> fn(x = 1.0)
(1.0, 2.0)

julia> fn(y = 1.0)
(1.0, 3.0)
6 Likes

Fantastic! This works perfectly. Putting everything together it would look something like this then:

struct OrderedPair
    x::Real
    y::Real
    
    OrderedPair(x::Float64,y::Float64) = new(x,y)
    OrderedPair(x::Int64,y::Int64) = new(x+1,y-1)

    OrderedPair(;x=nothing,y=nothing) = OrderedPair(x,y)
    OrderedPair(x::Float64, ::Nothing) = new(x,x+1)
    OrderedPair(::Nothing, y::Float64) = new(y,y+2)
end

Which gives the desired result:

julia> OrderedPair(1.0,1.0)
OrderedPair(1.0, 1.0)

julia> OrderedPair(1,1)
OrderedPair(2, 0)

julia> OrderedPair(x=1.0)
OrderedPair(1.0, 2.0)

julia> OrderedPair(y=1.0)
OrderedPair(1.0, 3.0)
2 Likes