Constructor of parametric types

I have the following two types. The second one is based on the first one.

struct RankOneMatrix{T} <: AbstractMatrix{T}
    v::AbstractVector{T}
    w::AbstractVector{T}
end

function Base.size(A::RankOneMatrix)
    return length(A.v), length(A.w)
end

struct RankTwoMatrix{T} <: AbstractMatrix{T}
    A::RankOneMatrix{T}
    B::RankOneMatrix{T}
    function RankTwoMatrix{T}(A::RankOneMatrix{T}, B::RankOneMatrix{T}) where T
        if size(A) != size(B)
            error("They have different dimensions.")
        end
        new(A, B)
    end
end

In the second type, I want the constructor to check the sizes of the two matrices are the same.
When I call RankTwoMatrix(RankOneMatrix(rand(3), rand(3)), RankOneMatrix(rand(3), rand(3))), I got the following error.

ERROR: LoadError: MethodError: no method matching RankTwoMatrix(::RankOneMatrix{Float64},::RankOneMatrix{Float64})

I think I do have the matching method.
I’ve read the manual but still have no idea what’s going on.
Please forgive me if this is a basic question.

First note that you can construct it when you are explicit with the type parameter:

julia> RankTwoMatrix{Float64}(RankOneMatrix(rand(3), rand(3)), RankOneMatrix(rand(3), rand(3)));

This is because when you define a custom inner constructor for a parameteric type, the outer (convenience) constructor is not automatically defined. You have to do that yourself:

julia> RankTwoMatrix(A::RankOneMatrix{T}, B::RankOneMatrix{T}) where T = RankTwoMatrix{T}(A::RankOneMatrix{T}, B::RankOneMatrix{T}) 
RankTwoMatrix

julia> RankTwoMatrix(RankOneMatrix(rand(3), rand(3)), RankOneMatrix(rand(3), rand(3)));

(note the ; are necessary as the displaying does not work with you minimal example (which is ok)).

1 Like

as @mauro3 noted above

You probably intended this:

struct rankTwoMatrix{T} <: AbstractMatrix{T}
           A::RankOneMatrix{T}
           B::RankOneMatrix{T}
           function rankTwoMatrix(A::RankOneMatrix{T}, B::RankOneMatrix{T}) where T
               if size(A) != size(B)
                   error("They have different dimensions.")
               end
               new{T}(A, B)
           end
       end

defined that way, your RankOneMatrix and RankTwoMatrix will initialize – but do not try to display them ranktwo = RankTwoMatrix(RankOneMatrix(rand(3,3)), RankOneMatrix(rand(3,3)));

1 Like

Great, that’s the reason. Thank you.
Is there a better way that I can add the check to the constructor?
The manual outer constructor

RankTwoMatrix(A::RankOneMatrix{T}, B::RankOneMatrix{T}) where T = RankTwoMatrix{T}(A::RankOneMatrix{T}, B::RankOneMatrix{T})

seems redundant to me.
More precisely, is it possible to define a outer constructor to check the sizes in this example?

See Jeffrey’s answer.

@JeffreySarnoff Wow, thanks, I think that’s what I want.
So in the definition foo{T}, the {T} is part of the name?
I guess this is why I didn’t get the matched method.

Thank you both. I learned a lot.

2 Likes