Julia: computed subtyping declarations

Created on 12 Sep 2014  路  7Comments  路  Source: JuliaLang/julia

Three things that would be nice to have:

  1. type Foo1{T} <: T end
  2. type Foo2{T} <: (T==Int ? Signed : Unsigned) end
  3. type Foo3{C}; a::C{Int} end

There are numerous related discussions on the lists, but I wanted to know if these requests are just not yet implemented or are "won't fix".
The problem is that at method/type definition time, type parameters are TypeVar objects and not the type they refer to at instanciation time.
The first time I hit this was when defining a method like fun{C}(collection::C, x::eltype{C}) = ... : there was no erros, because there is a fallback eltype(x) = Any, but this didn't do what I intended (this example seems related to point 2, but is maybe trickier to implement). Similarly it's unexpected that Foo2{Int}.super == Unsigned, this would deserve a clarification in the docs.
Another real use case of point 2. just came up from Bill Hart on julia-dev:

abstract Ring
abstract EuclideanDomain <: Ring 
abstract Field <: EuclideanDomain
type Polynomials{R<:Ring} <: (R<:Field ? EuclideanDomain : Ring) end

My hope that this gets implemented comes from the type section of the manual: "because Julia is a dynamically typed language and doesn鈥檛 need to make all type decisions at compile time, many traditional difficulties encountered in static parametric type systems can be relatively easily handled" :)

speculative

Most helpful comment

A simpler but related problem is arithmetic with integer type parameters, e.g. a trajectory in D dimensions is a D+1 dimensional object

immutable DynamicState{D}
    y :: Array{Float64,D}
end
immutable DynamicPath{D}
     yy :: Array{Float64,D+1}
end

All 7 comments

The problem with 1 and 2 is that Foo1 and Foo2 are themselves types, so they need a place in the type hierarchy. With this extension Foo1 by itself would not make sense as a type, which would be a very dramatic change.

In case 3, C{Int} is a problem since we don't know that type C will even have parameters. However this case is a bit more tractable, since it's not so important to know field types for un-instantiated types. In inference, we could just assume they are Any unless we know C.

For case 3, the very notation a::C{Int} could be a constraint that C is a parameterizable type. But more simply, like in C++ for so-called template template parameters, a special syntax may be used? e.g. type Foo3{C{}}; a::C{Int} end. I would favor having inference assume they are Any as you suggest, it seems no worse that the following work-around:

type Foo3{CI}
    a::CI
    Foo3(a) = (@assert eltype(CI) == Int; new(a))
end

For cases 1 and 2, I of course didn't think of the problem you point, but the same solution of making Foo1 and Foo2 subtypes of Any isn't applicable?

Making Foo1 a subtype of Any might work; I'll have to think about it.

Thanks. I realize I didn't give my real use case. My first try was relying on 3:

type SetLike{T, UnderlyingContainer} <: AbstractSet{T}
    cont::UnderlyingContainer{T}
end

So my second try was naturally using 2:

type SetLike{ConcreteUnderlying} <: AbstractSet{eltype{ConcreteUnderlying}}
    cont::ConcreteUnderlying
end

So I seem to not be able to express that my SetLike{T} is an AbstractSet{T}, unless I hardcode the underlying used container. This is not a big deal, but I like generality, whose price is unfortunately a loss in expressivity in this case.

@JeffBezanson I can give two more examples where these ideas are relevant. In #8176, we have been discussing if an array view should be a DenseArray or an AbstractArray. Here it could make sense with something like

View{T,S<:DenseArray{T}}<: DenseArray{T}
View{T,S<:AbstractArray{T}}<:AbstractArray{T}

or maybe even

View{T,S<:AbstractArray{T}}<:super(S)

In this thread, we discussed the possibility of storing a matrix of dual numbers as Dual{Matrix{Float64}} in order to leverage BLAS for the matrix arithmetic. However, that is not possible when Dual{T<:Real}<:Number. It would be great if it was possible to add something like the definition Dual{T<:AbstractMatrix{Float64}}<:AbstractMatrix{Float64}.

It appears to me that, in terms of the type lattice, this possibility is not invalid, although it changes the lattice structure a bit. It is different from the present system, but I think it could make sense that the location in the type hierarchy of a parametric type depends on the values of the parameters.

A simpler but related problem is arithmetic with integer type parameters, e.g. a trajectory in D dimensions is a D+1 dimensional object

immutable DynamicState{D}
    y :: Array{Float64,D}
end
immutable DynamicPath{D}
     yy :: Array{Float64,D+1}
end

If it were available, ColorTypes would use

abstract type Colorant{T,N} end    # T is element type, N is number of color channels
abstract type Color{T, N} <: Colorant{T,N} end    # types with no alpha channel
abstract type TransparentColor{T,N,C<:Color{T,N-1}} <: Colorant{T,N} end

where the N vs N-1 is due to the alpha channel.

Or even better:

abstract type Colorant{T,N::Int} end    # T is element type, N is number of color channels
abstract type Color{T,N::Int} <: Colorant{T,N} end    # types with no alpha channel
abstract type TransparentColor{T,N::Int,C<:Color{T,N-1}} <: Colorant{T,N} end

since that would allow one to express the fact that N must be a concrete Int.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

StefanKarpinski picture StefanKarpinski  路  3Comments

iamed2 picture iamed2  路  3Comments

ararslan picture ararslan  路  3Comments

manor picture manor  路  3Comments

sbromberger picture sbromberger  路  3Comments