1
votes

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?

1

1 Answers

1
votes

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}