You are right, it is not a clean REPL but now I’m confused. What I did is that I executed the two examples one after the other:
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
end
julia> SummedArray(Int32[1; 2; 3], Int32(6))
SummedArray{Int32,Int32}(Int32[1, 2, 3], 6)
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
function SummedArray(a::Vector{T}) where T
S = widen(T)
new{T,S}(a, sum(S, a))
end
end
julia> SummedArray(Int32[1; 2; 3], Int32(6))
SummedArray{Int32,Int32}(Int32[1, 2, 3], 6)
I’m confused, because I was thinking that the type was redefined, and the first version is different from the second one. But it is not the case:
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
end
julia> First = SummedArray
SummedArray
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
function SummedArray(a::Vector{T}) where T
S = widen(T)
new{T,S}(a, sum(S, a))
end
end
julia> Second = SummedArray
SummedArray
julia> result = SummedArray(Int32[1; 2; 3], Int32(6))
SummedArray{Int32,Int32}(Int32[1, 2, 3], 6)
julia> isa(result, Second)
true # I can understand this
julia> isa(result, First)
true # All right, this is suprising
julia> First === Second
true # Completely insane :-)
julia> methods(SummedArray)
# 2 methods for type constructor:
[1] (::Type{SummedArray})(data::Array{T,1}, sum::S) where {T<:Number, S<:Number} in Main at none:3
[2] (::Type{SummedArray})(a::Array{T,1}) where T in Main at none:9
So what happens if I give it a different name?
julia> struct SummedArray2{T<:Number,S<:Number}
data::Vector{T}
sum::S
function SummedArray(a::Vector{T}) where T
S = widen(T)
new{T,S}(a, sum(S, a))
end
end
julia> SummedArray2 === First
false # This makes sense
julia> SummedArray2 === Second
false # This makes sense too
So maybe it means that if I “redeclare” the type with the same name, then it won’t create a new type, but rather “extend” it somehow.
What will happen if I try to change its structure?
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
data2::Vector{T}
sum::S
function SummedArray(a::Vector{T}) where T
S = widen(T)
new{T,S}(a, sum(S, a))
end
end
ERROR: invalid redefinition of constant SummedArray
Stacktrace:
[1] top-level scope at none:0
Obviously it is not a bug, but it was unexpected, at least for me.
It means that one can add inner constructors after the type was defined
julia> struct Test
x :: Int64;
function Test(x::Int64)
return new(x)
end
end
julia> Test(1)
Test(1)
julia> Test(1.0)
ERROR: MethodError: no method matching Test(::Float64)
Closest candidates are:
Test(::Int64) at none:4
Stacktrace:
[1] top-level scope at none:0
julia> struct Test
x :: Int64;
# Please note that here we are adding an inner constructor,
# despite the fact that the Test type is already defined!
function Test(x::Float64)
return new(convert(Int64,x))
end
end
julia> Test(1.0)
Test(1)
julia> methods(Test)
# 2 methods for type constructor:
[1] Test(x::Int64) in Main at none:4
[2] Test(x::Float64) in Main at none:6
julia>
It might be considered a violation of the rules, but I don’t think that it is a design flaw or a problem. But definitely very interesting.
I learnt something new. Thank you!