Hi all, I am studying how Julia handles constructors in parametric types and have found a puzzling behavior.
Consider this trivial type:
struct Test1{R <: Real}
x::R
end
I can instantiate Test1
either with or without stating R
explicitly:
# Works
println(Test1(1.0))
# Works
println(Test1{Float64}(1.0))
If I provide an explicit inner constructor, the first case no longer works but the second one does:
struct Test2{R <: Real}
x::R
Test2{R}(r::R) where {R <: Real} = new{R}(r)
end
# Does not work, of course!
# println(Test2(1.0))
println(Test2{Float64}(1.0))
Here comes the puzzling part: suppose that I want my type to handle the case when the caller passes a complex number by just taking its real part:
struct Test3{R <: Real}
x::R
function Test3{C}(z::C) where {C <: Complex}
r = real(z)
R = typeof(r)
return new{R}(r)
end
end
# Why does this not work?
println(Test3{ComplexF64}(1.0 + 2.0im))
Although it seems to me that Test3
closely matches the way the inner constructor for Test2
was defined, this does not work and produces this error:
ERROR: LoadError: TypeError: in Test3, in R, expected R<:Real, got Type{ComplexF64}
The way to solve this problem is to drop {C}
from the inner constructor:
struct Test4{R <: Real}
x::R
# No `Test4{C}` here, just `Test4`
function Test4(z::C) where {C <: Complex}
r = real(z)
R = typeof(r)
return new{R}(r)
end
end
# Works!
println(Test4(1.0 + 2.0im))
I am curious about what’s happening in Test3
: I do not understand why Julia still requires that the input type be <: Real
when the inner constructor clearly says that it can be <: Complex
. May somebody help me understand Julia’s behavior here?