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:
- Why version 3 is not a syntax error?
- 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
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
The end. Questions at top of the post.