The third function would only work for inputs of type Point{<:Integer} but what is the difference between the first and the second definition?

If I wanted to define a function that works for all T, would I rather choose a definition such as coordinates1 where I drop the specification of the type, or would I go for a definition such as coordinates2 where I copy the restriction T<:Real from the definition of the parametric struct Point?

The methods coordinates1 and coordinates2 are exactly the same. In fact if you give them the same name:

julia> coordinates(p::Point) = (p.x, p.y)
coordinates (generic function with 1 method)
julia> coordinates(p::Point{<:Real}) = (p.x, p.y)
coordinates (generic function with 1 method)
julia> methods(coordinates)
# 1 method for generic function "coordinates":
[1] coordinates(p::Point) in Main at REPL[11]:1

you will see that in the end only one method is really defined.
This changes when you introduce the coordinates3 signature

julia> coordinates(p::Point{<:Integer}) = (p.x, p.y)
coordinates (generic function with 2 methods)
julia> methods(coordinates)
# 2 methods for generic function "coordinates":
[1] coordinates(p::Point{<:Integer}) in Main at REPL[13]:1
[2] coordinates(p::Point) in Main at REPL[11]:1

Ofc you give different names, so in your case all 3 function exist. Just wanted to say that there is no difference in the argument types between the the first and the second.

Since you defined Point{T<:Real} you can only ever define Point with T<:Real. And as mentioned previously it makes no difference whether you use the first or the second signature. I personally find the first more readable, as there you do not repeat yourself.

Hi @weltenbummler, could you mark the second post as the solution to this thread? This way is easier to find for new people that may have the same question.