Long Intro to Julia: type tutorial exercise


#1

To be honest, I didn’t quite get this tutorial. I am referring to this one about type, which I think is critical

I got to this exercise towards the end

Define a UnitPoint{<:Real} parametric struct type which has x and y fields of type T and which has an inner constructor that normalizes its arguments by diving them by hypot(x, y) upon construction, guaranteeing that the resulting point is on the unit circle.

my attempt below (it’s called UnitPoint3 because the previous attempts seems to prevent me from redefining a struct, so anyone who knows how to redefine a struct can lend a hand thanks).

struct UnitPoint3{T<:Real}
    x :: T
    y :: T
    function UnitPoint3(x::T,y::T) 
        distance = hypot(x,y)
        return new(x / distance, y / distance)
    end

end

Now this code gave an error syntax: too few type parameters specified in "new{...}"

How should I fix this? Is there a more step by step intro to the type system?


#2

You need new{T}, this T is the type parameter it is complaining about. You will also need a where T at the top of function defn, I think.


#3

The following should work:

struct UnitPoint3{T<:Real}
    x :: T
    y :: T
    function UnitPoint3{T}(x,y) where {T<:Real}
        distance = hypot(x,y)
        return new(x / distance, y / distance)
    end
end
julia> UnitPoint3{Float64}(1,1)
UnitPoint3{Float64}(0.7071067811865475, 0.7071067811865475)

The documentation contains more details about Parametric Constructors.


#4

That version of the introductory materials is a brand new iteration of the content, and we’re still working on improving them. Indeed some of the exercises are far too difficult without more description. Sorry you got tripped up by it! @ffevotte’s solution is a good one.

Just to answer your other question: it’s unfortunately not possible to redefine a struct. You can re-evaluate it as long as you don’t change it significantly, but of course as you’re trying different things of course you want to significantly change the definition. The workaround you found (just iteratively changing names) is one I’ve used in the past — once I get a form I like I just rename everything back to the name I originally wanted and restart. Another workaround is to use modules since they can be replaced, but that can get tricky fast as you can end up with objects that are still the old type but have (what looks to be) the same name.


#5

@ffevotte many thanks for your tip!

I tried your solution and indeed it worked for UnitPoint3{Float64}(1,1), but it errored when I tried UnitPoint3{Int64}(1,1) with a message InexactError: Int64(Int64, 0.7071067811865475)

if this is the intended behavior, then wouldn’t this parameterization limit the struct rather than make it more general?

thanks!


#6

try this

function unitized(x::T, y::T) where {T<:Real}
  h = hypot(x, y)
  return x/h, y/h
end
    
struct UnitPoint{T<:Real}
  x::T
  y::T

  function UnitPoint(x::T, y::T) where {T<:Real}
     ux, uy = unitized(x, y)
     return new{float(T)}(ux, uy)
  end
end
julia> UnitPoint(1,1)
UnitPoint{Float64}(0.7071067811865475, 0.7071067811865475)

julia> UnitPoint(1.0,1.0)
UnitPoint{Float64}(0.7071067811865475, 0.7071067811865475)

julia> UnitPoint(1//1, 1//1)
UnitPoint{Float64}(0.7071067811865475, 0.7071067811865475)

julia> UnitPoint(1.0f0, 1.0f0)
UnitPoint{Float32}(0.70710677f0, 0.70710677f0)

hypot(x,y) always returns a floating point type (it takes a sqrt).