Problem with parametric composite types

I defined a type:

struct Point{T}
x :: T 
y :: T 
end 

Then when I tried to construct an variable

b = Point(1.0, 2)

I got

ERROR: MethodError: no method matching Point(::Float64, ::Int64)

Closest candidates are:
  Point(::T, ::T) where T
   @ Main REPL[2]:2

The problem is, aren’t ‘Float64’ and ‘Int64’ both subtypes of ‘Any’? The construction should work if we interprete ‘T’ as ‘Any’, right?

You’re restricting x and y to have the same concrete type T. If you want them to be independent, do Point{T,S}.

2 Likes

For an explanation of concrete vs abstract types see:

https://docs.julialang.org/en/v1/manual/types/#man-abstract-types

In fairness though, I can see how this would be confusing. You should be able to construct a Point{Real}(1.0, 2) but I guess the compiler won’t auto infer parameterized types as abstract types?

What you see is the so-called “diagonal rule” in action.

Implicitly, Julia defines a constructor Point(x::T, y::T) where {T} = Point{T}(x, y). The diagonal rule is that if a type parameter occurs multiple times in a function signature, then those occurences mean the same concrete type.

You can still manually create Point{Any}(1, 2.0) or add more constructors to your liking.

2 Likes

Note that this uses a different constructor from the one with the diagonal rule, which isn’t recursive despite how it looked.

julia> struct Point{T}
       x :: T 
       y :: T 
       end 

julia> Point(1, 1) # parameter inferred from arguments
Point{Int64}(1, 1)

julia> @which Point(1, 1) # diagonal rule method
Point(x::T, y::T) where T in Main at REPL[29]:2

julia> Point{Real}(1, 1.0) # manually specify parameter
Point{Real}(1, 1.0)

julia> @which Point{Real}(1, 1.0) # manual parameter method
Point{T}(x, y) where T in Main at REPL[29]:2
2 Likes