On Friday, August 22, 2014 01:17:53 PM Jameson Nash wrote: > Instead of eltype as written, use the following format: > eltype{T<:...}(::Type{T}) = eltype(super(T))
With this definition: Base.eltype{T<:MyAbstractType}(::Type{T}) = eltype(super(T)) eltype(MyType{Float32}) yields "Any". And for the second: julia> f{M<:MyAbstractType}(::Type{M}) = (M.name){Float32} f (generic function with 1 method) julia> f(MyType{Float64}) ERROR: type: instantiate_type: expected TypeConstructor, got TypeName in f at none:1 It was a good suggestion, it's just that I had already tried it. --Tim > > 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