Reputation: 1044
I specified a custom AbstractArray
type with additional generic parameters.
I wanted to make an alias for matrices and use an existing outer constructor,
but stumbled upon such a behaviour:
struct MyArray{T,N,K} <: AbstractArray{T,N}
t::T
end
MyMatrix{T} = MyArray{T,2,1}
MyArray{T,N,K}(x::AbstractArray{T,N}) where {T,N,K} = MyArray{T,N,K}(x[1])
mat = MyMatrix([1 2; 3 4])
MethodError: no method matching MyArray{T,2,1} where T(::Array{Int64,2})
To me, a constructor call with type MyArray{T,2,1} where T(::Array{Int64,2})
should be able to use
the one already declared.
I had a couple of other outer constructors, but all for the generic MyArray
(and apparently not any of the ones that would make the preceding code legal).
If I declare a specific constructor everything works:
MyMatrix(x::AbstractMatrix{T}) where {T} = MyMatrix{T}(x[1])
but it seems redundant to have constructors for aliases.
Similarly, I can specify the type with the constructor call:
mat = MyMatrix{Int}([1 2; 3 4])
but here the information is also repeated, as the type can be deduced from the argument.
Is there a way for me not to repeat the types or specialize the constructors when I use the matrix type?
Upvotes: 1
Views: 124
Reputation: 1390
The takeaway message is that MyMatrix(x)
doesn't know how to infer the parameter T
. But you can always explicitly define the desired behavior.
It takes me a while to understand your question, so correct me if I'm wrong.
Let's say we have only defined the MyArray
and the alias MyMatrix
:
struct MyArray{T,N,K} <: AbstractArray{T,N}
t::T
end
MyMatrix{T} = MyArray{T,2,1}
Simply call MyMatrix(0)
will trigger the error:
ERROR: MethodError: no method matching MyArray{T,2,1} where T(::Int64)
I guess that you may assume the type parameter T
can be inferred to be of Int64
. Unfortunately not. But you can just define a constructor like this:
MyMatrix(x::T) where T = MyMatrix{T}(x)
Now the second question is that we have a default constructor for MyArray
:
MyArray{T,N,K}(x::AbstractArray{T,N}) where {T,N,K} = MyArray{T,N,K}(x[1])
And you wish that MyMatrix([1 2; 3 4]) === MyMatrix{Int}(1)
. But in fact, it will only call the method we just defined:
julia> @which MyMatrix([1 2; 3 4])
(::Type{MyArray{T,2,1} where T})(x::T) where T in Main at REPL[3]:1
So we can define another more specific method (but not that specific like the one you provide ;)
MyMatrix(x::AbstractArray{T}) where T = MyMatrix{T}(x)
Then it will fall back to your general definition of MyArray{T,N,K}(x::AbstractArray{T,N}) where {T,N,K} = MyArray{T,N,K}(x[1])
julia> @code_typed MyMatrix([1 2; 3 4])
CodeInfo(
1 ─ %1 = (Base.arrayref)(true, x, 1)::Int64
│ %2 = %new(MyArray{Int64,2,1}, %1)::MyArray{Int64,2,1}
└── return %2
) => MyArray{Int64,2,1}
Upvotes: 1