Inspect parameter type in container-like class

I have a very common problem for which I still don’t have a clear solution in Julia:

abstract AbstractFoo{N,T}
dimension{N,T}(::AbstractFoo{N,T}) = N
coordtype{N,T}(::AbstractFoo{N,T}) = T

abstract AbstractContainer{F}

immutable Container{F<:AbstractFoo} <: AbstractContainer{F}
  state::Vector{coordtype(F)} # ERROR: MethodError: no method matching coordtype(::TypeVar)
end

The natural design above in which the container is parametrized by its element type doesn’t work. This problem was initially introduced in another thread but I think it deserves a thread on its own as well as a step-by-step explanation on what is the best current solution in Julia v0.5.

Issue #18466 linked in the other thread answers the question: this is not currently possible. Several workable suggestions were given there.

Thank you @ihnorton, I am looking for the best workable suggestion, it is still not clear to me.

The current solution seems to be roughly this:

  1. for each transformation/constraint calculated in type space, introduce a new type parameter
  2. check that in the inner constructor

Something along the lines of

abstract AbstractFoo{N,T}
dimension{N,T}(::AbstractFoo{N,T}) = N
coordtype{N,T}(::AbstractFoo{N,T}) = T

abstract AbstractContainer{F}

immutable Container{F<:AbstractFoo,S} <: AbstractContainer{F}
    state::Vector{S}
    function Container(state)
        @assert S == coordtype(F)
        new(state)
    end
end

if I understood your problem correctly.

I used to think that this was tedious, but I now think that it is the best way to do it, because there are some constraints you can’t express in a triangular manner anyway.

1 Like

Thank you @Tamas_Papp, what do you mean by transformation/constraint?

coordtype above is a transformation (function): it maps F to S.

More generally, one could enforce that some g(S,F) holds, even when that is not amenable to a S=h(F) mapping.

1 Like

Do you have an example in which a more general relation g(S,F) is needed?

Suppose that for some calculation with floats, I want F to be at least as precise as S (in the sense of Float16 < Float32 < Float64 < ...), but I don’t want to determine how precise (it’s up to the user), other than “at least Float32”. So I could do

function g(S,F)
    if S <: AbstractFloat && F <: AbstractFloat
        _rank(f) = findfirst([Float16, Float32, Float64, BigFloat], f)
        max(_rank(S),2) ≤ _rank(F)
    else
        false
    end
end

and enforce this with the constructor.

1 Like