Parametric types and where keyword in Julia v0.6

question

#1

The following typealias used to work in Julia v0.5:

typealias RealLowerTriangular{T<:Real, S<:AbstractMatrix} LowerTriangular{T, S}

Upon upgrading to Julia v0.6, I received a suggestion on the REPL to use instead the following:

RealLowerTriangular{T<:Real, S<:AbstractMatrix} = LowerTriangular{T, S}

However, trying out the supposed fix yields the following error:

RealLowerTriangular(ones(2))
ERROR: MethodError: Cannot convert an object of type Array{Float64,1} to an object of type LowerTriangular{T,S} where S<:(AbstractArray{T,2} where T) where T<:Real
This may have arisen from a call to the constructor LowerTriangular{T,S} where S<:(AbstractArray{T,2} where T) where T<:Real(…),
since type constructors fall back to convert methods.
Stacktrace:
[1] LowerTriangular{T,S} where S<:(AbstractArray{T,2} where T) where T<:Real(::Array{Float64,1}) at ./sysimg.jl:24


#2

That’s the same in 0.5

julia> typealias RealLowerTriangular{T<:Real, S<:AbstractMatrix} LowerTriangular{T, S}
LowerTriangular{T<:Real,S<:AbstractArray{T,2}}

julia> RealLowerTriangular(ones(2))
ERROR: MethodError: Cannot `convert` an object of type Array{Float64,1} to an object of type LowerTriangular{T<:Real,S<:AbstractArray{T,2}}
This may have arisen from a call to the constructor LowerTriangular{T<:Real,S<:AbstractArray{T,2}}(...),
since type constructors fall back to convert methods.
 in LowerTriangular{T<:Real,S<:AbstractArray{T,2}}(::Array{Float64,1}) at ./sysimg.jl:53

#3

That’s because there was a small typo in my example, sorry. Here is what I meant, which I just tried it in Julia v0.5.2 and it works:

julia> typealias RealLowerTriangular{T<:Real, S<:AbstractMatrix} LowerTriangular{T, S}
LowerTriangular{T<:Real,S<:AbstractArray{T,2}}

julia> RealLowerTriangular(ones(2, 2))
2×2 LowerTriangular{Float64,Array{Float64,2}}:
 1.0   ⋅ 
 1.0  1.0

#4

maybe it’s because ones(2,2) is not lower triangular, and older version had bug?


#5

Thanks Dan, this wasn’t meant to be an accurate example, you are right that ones(2, 2) is not lower triangular. What I am getting at is that the equivalent proposed by Julia v0.6 no longer works, one could use for instance eye(2) above instead of ones(2, 2) to make this point.


#6

Indeed there is a problem getting to the constructor of LowerTriangular from the typedef-ed RealLowerTriangular. A solution can be to define the outer constructor:

# first we do the typedef
RealLowerTriangular{T,S} = LowerTriangular{T,S} where {T<:Real,S<:AbstractMatrix{T}}

# now the outer constructor
RealLowerTriangular(m) = LowerTriangular(m)

The logic is as follows: Calling a constructor is basically calling a method with the type as its name. The outer constructor LowerTriangular is simply a method named LowerTriangular{T,S} where S where T. A RealLowerTriangular is defined as LowerTriangular{T,S} where S<:AbstractMatrix{T} where T<:Real so Julia doesn’t find a method with this name and defaults to the convert constructor for a generic type.

In general, the non-parametrized names of parametric types are equivalent to the types with appropriate where T clauses added (for each parameter).

Hope this somehow makes more sense.
And

julia> RealLowerTriangular(ones(2,2))
2×2 LowerTriangular{Float64,Array{Float64,2}}:
 1.0   ⋅ 
 1.0  1.0

works with these definitions.


#7

Brilliant, this makes sense, and works.