Constructor bug, what am I missing?

This construct bug is so strange, I can’t find where I am wrong.

struct tcg{F,T}
    a::F
    b::T
    tcg(b::T,a::F=nothing) where {F,T}=new{F,T}(a,b)
    tcg(a::F,b::T) where {F,T}=new{F,T}(a,b)
end
tcg(10)

The outcome should be this?

tcg{Nothing,Int64}(nothing,10)

But acctually, it is:

tcg{Int64, Nothing}(10, nothing)

Where am I going wrong?

Case 1:

struct tcg{F,T}
    a::F
    b::T
    #= tcg(b::T,a::F=nothing) where {F, T} = begin
        @show :tcg1
        new{F,T}(a,b)
    end =#
    tcg(a::F,b::T) where {F, T} = begin
        @show :tcg2
        new{F,T}(a,b)
    end
end

tcg(10)

yields the expected

ERROR: MethodError: no method matching tcg(::Int64)

Case 2:

struct tcg{F,T}
    a::F
    b::T
    tcg(b::T,a::F=nothing) where {F, T} = begin
        @show :tcg1
        new{F,T}(a,b)
    end
    tcg(a::F,b::T) where {F, T} = begin
        @show :tcg2
        new{F,T}(a,b)
    end
end

tcg(10)

yields

:tcg2 = :tcg2

This seems to be a worthy new issue.

To workaround the problem for the time being you could use

struct tcg{F,T}
    a::F
    b::T
    tcg(b::T,a::Nothing) where {T} = begin
        @show :tcg1
        new{Nothing,T}(a,b)
    end
    tcg(a::F,b::T) where {F, T} = begin
        @show :tcg2
        new{F,T}(a,b)
    end
end

tcg(10)

yielding

:tcg1 = :tcg1
1 Like

This is documented and intentional behaviour: Methods · The Julia Language

Basically f(x, y=default) = ... is just shorthand for two method definitions:

f(x, y) = ...
f(x) = f(x, default)

In your case, your constructors are equivalent to this:

tcg(b::T, a::F) where {F,T} = new{F,T}(a,b)
tcg(b::T) where {F,T} = tcg(b, nothing)
tcg(a::F, b::T) where {F,T} = new{F,T}(a,b)

and since F and T are unconstrained, the third method overwrites the first one, so that tcg(b::T) calls the third method.

I think you probably want these two constructors:

tcg(b) = tcg(nothing, b)
tcg(a::F, b::T) where {F,T} = new{F,T}(a,b)
3 Likes