Thanks everyone for the replies.

Jiahao:  Thanks especially for the pointer to MAX_TYPE_DEPTH. This is 
exactly what I hit, and now I'm trying to learn what its consequences are.

I appreciate the need for constraints to keep type inference efficient, but 
I'm not sure why what I am doing is running up against them. The root 
problem seems to be at the level of T5's constructor.  With the same type 
hierarchy as in the original post, constructing a T4 is not a problem.

*julia> **@code_warntype T4(T3(T2(T1{Float64}())))*

Variables:

  x::T3{T2{T1{Float64}}}


Body:

  begin 

      return $(Expr(:new, T4{T3{T2{T1{Float64}}}}, 
:(x::T3{T2{T1{Float64}}})))

  end::T4{T3{T2{T1{Float64}}}}


But constructing a T5 is a problem, even if I tell the compiler exactly 
what kind of T5 I am constructing.

*julia> **t4 = T4(T3(T2(T1{Float64}())));*


*julia> **@code_warntype T5(t4)*

Variables:

  x::T4{T3{T2{T1{Float64}}}}


Body:

  begin 

      return ((top(apply_type))(T5,T)
*::Type{_<:T5{T<:T4{T<:T3{T<:T2{T<:T1{I<:Number}}}}}}*
)(x::T4{T3{T2{T1{Float64}}}})*::T5{T<:T4{T<:T3{T<:T2{T<:T1{I<:Number}}}}}*

  end*::T5{T<:T4{T<:T3{T<:T2{T<:T1{I<:Number}}}}}*

*julia> **@code_warntype T5{T4{T3{T2{T1{Float64}}}}}(t4)*

Variables:

  #s2::Type{T5{T4{T3{T2{T1{Float64}}}}}}

  x::T4{T3{T2{T1{Float64}}}}


Body:

  begin 

      return $(Expr(:new, 
:((top(apply_type))((top(getfield))(Main,:T5)::Type{T5{T<:T4{T<:T3{T<:T2{T<:T1{I<:Number}}}}}},T)::Type{_<:T5{T<:T4{T<:T3{T<:T2{T<:T1{I<:Number}}}}}}),
 
:(x::T4{T3{T2{T1{Float64}}}})))

  end*::T5{T<:T4{T<:T3{T<:T2{T<:T1{I<:Number}}}}}*

I would have thought in this last case there was nothing left to infer.  Is 
this last case expected behavior? Is there a way to construct a T5 such 
that the type is known to the type inference system? (Without bumping up 
MAX_TYPE_DEPTH; I'd like to learn to live within the default limits!)

Without delving too far into inference.jl, I had worried that maybe type 
inference just punts when it encounters types exceeding MAX_TYPE_DEPTH. 
 But that seems to be not true.  The functions I've attempted seems to 
reason along happily about T5's once they are constructed.  Here's a 
trivial example.

*julia> **function id(x::T5) x end*

*id (generic function with 1 method)*


*julia> **t5 = T5(t4);*


*julia> **@code_warntype id(t5)*

Variables:

  x::T5{T4{T3{T2{T1{Float64}}}}}


Body:

  begin  # none, line 1:

      return x::T5{T4{T3{T2{T1{Float64}}}}}

  end::T5{T4{T3{T2{T1{Float64}}}}}

More interesting examples require modifying the types in the hierarchy so 
that they are more complex, but the few things I've tried (without pushing 
too hard) seem ok, once a T5 is in hand.

Anyway, in practice I find I use the idiom

abstract AbstractClass

type C{A<:AbstractClass}
  x::A
  more fields....
end

so that the explicit type of field x is known if the type of C is known. 
 But if I understand the above correctly, it seems that C's constructor is 
not type stable, in the sense that there are constructors for kinds of C's 
such that the return type is not known to the type system, even when all 
the argument types to the constructor are known. Is this correct?

Many thanks,

David

Reply via email to