Quantcast
Channel: First steps - JuliaLang
Viewing all articles
Browse latest Browse all 2795

Scope of parametric types and associated error messages

$
0
0

Hello, first post here. Sorry it’s so long.

I’m a little confused about the errors I’m getting with different levels of type annotations on a struct with an inner constructor. I have actually solved the problem but I have decided to document my thought process here because I have found some error messages to be quite unhelpful.

My main question is: What is the scope of parametric types? I think this is actually the source of my confusion.

Other questions:

  1. Why version 3 is not a syntax error?
  2. Why version 5 marks the error at the struct declaration insted of at the constructor? This version is the one that bothers me the most. Previous versions had dumb mistakes but this one I think is quite reasonable, although incorrect. Also I think the error message is totally misguiding.

Let’s start simple and working without parametric types:

This works all right:

VERSION 1

module ConfusingTypeErrors

struct Factorization
    users:: AbstractMatrix
    items:: AbstractMatrix

    function Factorization(n_users::Integer, n_items::Integer, n_components::Integer)
        return new(
            randn(n_users, n_components),
            randn(n_items, n_components))
    end
end

test = Factorization(100, 10, 32)

end

ERROR 1
No error :slight_smile:

By default randn will create Float64 numbers but I want to specify Float32 also if I desire.
I add some type parameters and I get an error I understand but I paste it here anyway so you can follow my reasoning. I have marked the lines with changes:

VERSION 2

module ConfusingTypeErrors

struct Factorization{T<:Real}  # <----
    users:: AbstractMatrix{T}    # <----
    items:: AbstractMatrix{T}    # <----

    function Factorization(n_users::Integer, n_items::Integer, n_components::Integer)
        return new(
            randn(n_users, n_components),
            randn(n_items, n_components))
    end
end

test = Factorization(100, 10, 32)

end

This gets a nice error:
ERROR 2

ERROR: LoadError: syntax: too few type parameters specified in "new{...}" around /home/plf/projects/recjul/test.jl:3

No problem, I must of course specify the type in new since my struct has a type parameter now:

VERSION 3

module ConfusingTypeErrors

struct Factorization{T<:Real}
    users:: AbstractMatrix{T}
    items:: AbstractMatrix{T}

    function Factorization(n_users::Integer, n_items::Integer, n_components::Integer)
        return new{T}( # <----
            randn(T, n_users, n_components), # <---
            randn(T, n_items, n_components)) # <---
    end
end

test = Factorization(100, 10, 32)

end

I get a new error. It seems T is defined when specifying member types but not for the inner constructor. Notice that this is not a syntax error and if I delete the test call to the constructor I get no error when loading the module.

ERROR 3

ERROR: LoadError: UndefVarError: T not defined
Stacktrace:
 [1] Main.ConfusingTypeErrors.Factorization(::Int64, ::Int64, ::Int64) at /home/plf/projects/recjul/test.jl:8

On one hand I find it counterintuitive but after some thought it seems OK since I should be calling test = Factorization{Float32}(100, 10, 32) and it makes sense that the definition of the constructor has the same template as the call. So let’s add first the type to the call:

VERSION 4

module ConfusingTypeErrors

struct Factorization{T<:Real}
    users:: AbstractMatrix{T}
    items:: AbstractMatrix{T}

    function Factorization(n_users::Integer, n_items::Integer, n_components::Integer)
        return new{T}(
            randn(T, n_users, n_components),
            randn(T, n_items, n_components))
    end
end

test = Factorization{Float32}(100, 10, 32)  # <---

end

Now I’m getting a little annoyed with this error:
ERROR 4

ERROR: LoadError: MethodError: no method matching Main.ConfusingTypeErrors.Factorization{Float32}(::Int64, ::Int64, ::Int64)

My hypothesis: I have defined a constructor without type parameters and I’m making a function call with type parameters and so they are not matching. It would have been nice nonetheless that the error message indicated the most similar ones as it does when not matching argument types.

Let’s add the type parameter then in the definition:

VERSION 5

module ConfusingTypeErrors

struct Factorization{T<:Real}
    users:: AbstractMatrix{T}
    items:: AbstractMatrix{T}

    function Factorization{T}(n_users::Integer, n_items::Integer, n_components::Integer) # <---
        return new{T}(
            randn(T, n_users, n_components),
            randn(T, n_items, n_components))
    end
end

test = Factorization{Float32}(100, 10, 32)

end

Now, I was really really hoping that should work but this is not the case. And actually the stacktrace points the error at the T not in the constructor but in the struct declaration at the top, which I find really really weird:

ERROR 5

ERROR: LoadError: UndefVarError: T not defined
Stacktrace:
 [1] top-level scope at /home/plf/projects/recjul/test.jl:3

So…, yes, if a function has a type parameter it should be declared with a where at the right so now adding a where {T<:Real} at the right of the constructor does the job. This works and is the final version:

VERSION 6

module ConfusingTypeErrors

struct Factorization{T<:Real}
    users:: AbstractMatrix{T}
    items:: AbstractMatrix{T}

    function Factorization{T}(n_users::Integer, n_items::Integer, n_components::Integer) where {T<:Real}
        return new{T}(
            randn(T, n_users, n_components),
            randn(T, n_items, n_components))
    end
end

test = Factorization{Float32}(100, 10, 32)

end

ERROR 6
No error :slight_smile:

The end. Questions at top of the post.

1 post - 1 participant

Read full topic


Viewing all articles
Browse latest Browse all 2795

Trending Articles