Instead of eltype as written, use the following format: eltype{T<:...}(::Type{T}) = eltype(super(T))
This should help with the type inference issue too. I think M.name is already the type, so you don't need to use eval (which doesn't return the right thing anyways, in the general case) Sorry I can't type more clearly on a phone, but I'm hoping this will be enough to get you going in the right direction. On Friday, August 22, 2014, Tim Holy <tim.h...@gmail.com> wrote: > Hi all, > > I've come up against a couple of surprising type-manipulation issues and > I'm > wondering if I'm just missing something. Hoping someone out there knows a > better way of doing these manipulations. > > The issues arise when I try writing generic algorithms on abstract > parametric > types that need to make decisions based on both the concrete parameters and > the concrete type. Let's start with a simple example: > > abstract MyAbstractType{T} > > immutable MyType{T} <: MyAbstractType{T} > a::T > b::T > end > > Now let's write a generic "eltype" method: > > Base.eltype{M<:MyAbstractType}(::Type{M}) = M.parameters[1] > > It's a little ugly to have to use M.parameters[1] here. While I'm curious > to > know whether I'm missing a simple alternative, I can live with this. > > Now let's define some arithmetic operations: > > (*)(x::Number, m::MyType) = MyType(x*m.a, x*m.b) > (.*)(x::Number, m::MyType) = MyType(x*m.a, x*m.b) > > And then try it out: > julia> y = MyType(3,12) > > julia> 2y > MyType{Int64}(6,24) > > julia> 2.1y > MyType{Float64}(6.300000000000001,25.200000000000003) > > > So far, so good. Now let's try something a little more sophisticated: > > julia> A = [MyType(3,12), MyType(4,7)] > 2-element Array{MyType{Int64},1}: > MyType{Int64}(3,12) > MyType{Int64}(4,7) > > julia> 2A > 2-element Array{Any,1}: > MyType{Int64}(6,24) > MyType{Int64}(8,14) > > The problem here is we're getting an Array{Any,1} back. No problem you say, > let's try to help type inference out, by specifying that a > ::MyType{Int}*::Float64 is a MyType{Float64}. If we were willing to write > this > using concrete types, it would be easy: > > Base.promote_array_type{T<:Real, S}(::Type{T}, ::Type{MyType{S}}) = > MyType{promote_type(T, S)} > > But if we want to just use the abstract type, then I've been unsuccessful > in > avoiding this construction: > > Base.promote_array_type{T<:Real, M<:MyAbstractType}(::Type{T}, ::Type{M}) = > eval(M.name.name){promote_type(eltype(M), T)} > > Ouch! Do we really have to use eval there? This seems likely to be a > runtime > bottleneck, which seems confirmed by > > code_llvm(Base.promote_array_type, (Type{Float64}, Type{eltype(A)})) > > Compare it against the version generated when I instead use the concrete > type, > and you'll see what I mean. > > Of course these issues would be easily solved by triangular dispatch or > staged > functions, but I'm hoping there's another good way that exists today. > > --Tim > >