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
>
>

Reply via email to