Type inference with parametric composite types

#1

In the example below, why can the type of x be inferred for S1 but not for S2? Is this behavior documented anywhere or is it a bug?

julia> struct S1
           x::Vector{Int}
       end

julia> struct S2{N}
           x::Vector{Int}
           y::Vector{SVector{N,Int}}
       end

julia> S1([])
S1(Int64[])

julia> S2([], SVector{3,Int}[])
ERROR: MethodError: no method matching S2(::Array{Any,1}, ::Array{SArray{Tuple{3},Int64,1,3},1})
Closest candidates are:
  S2(::Array{Int64,1}, ::Array{SArray{Tuple{N},Int64,1,N},1}) where N at REPL[3]:2
Stacktrace:
 [1] top-level scope at REPL[5]:1

julia> S2(Int[], SVector{3,Int}[])
S2{3}(Int64[], SArray{Tuple{3},Int64,1,3}[])
#2

[] is a vector of zero length. SVector{3,Int}[] asks for a StaticVector with 3 Ints to be obtained from a vector of 0 Ints.
Use @SVector([1,2,3]) if that’s what you want.

julia> s2 = S2([1,2,3],[@SVector([1,2,3,4])])
S2{4}([1, 2, 3], SArray{Tuple{4},Int64,1,4}[[1, 2, 3, 4]])

also, you do not need to wrap the SVector in a Vector

struct S2{N}
       x::Vector{Int}
       y::SVector{N,Int}
 end

julia> S2([1,2], @SVector([2,4])
S2{2}([1,2], [2,4])
#3

This is a bit subtle because the default outer constructor looks a bit different for the parametric vs non-parametric cases. I’m a bit hazy on this myself as the documentation at https://docs.julialang.org/en/v1/manual/constructors/index.html#Parametric-Constructors-1 isn’t very explicit about the exact form for the default outer constructor.

In this particular case, I think the default outer constructor for S2 is

S2(x::Array{Int64,1}, y::Array{SArray{Tuple{N},Int64,1,N},1}) where {N} = S2{N}(x,y)

Whereas for S1, the default outer constructor is

S1(x) = S1(convert(Vector{Int}, x))

So you can see why one case will match and the other will not. It does seem inconvenient though that the default outer constructor for S2 doesn’t match and just use convert. The following issue suggests that it was to make it harder to accidentally introduce infinite recursion when defining extra constructors: https://github.com/JuliaLang/julia/issues/10641.

1 Like
#4

Just to clarify, I did want y to be a zero length Vector of SVector{3,Int}s.

I think Chris’s response about S1 using convert clears my confusion.