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:
- for each transformation/constraint calculated in type space, introduce a new type parameter
- 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