Why are the inferred types different between collect and comprehension?

julia> using ApproxFun

julia> A = Derivative(); B = Multiplication(Fun());

julia> ops = (A, B);

julia> [ops...] |> eltype
Operator{Float64}

julia> collect(ops) |> eltype
Operator

Shouldn’t these lead to the same result?

Note that that is not a comprehension.

The [] array constructor promotes argument types, but collect and the comprehension evidently do not:

julia> [1, 1.0, 1f0]
3-element Vector{Float64}:
 1.0
 1.0
 1.0

julia> collect((1, 1.0, 1f0))
3-element Vector{Real}:
 1
 1.0
 1.0f0

julia> [x for x in ((1, 1.0, 1f0))]
3-element Vector{Real}:
 1
 1.0
 1.0f0

Look at the lowered code for [1, 1.0, 1f0]:

julia> @code_lowered [1, 1.0, 1f0]
CodeInfo(
1 ─      T = Core._apply_iterate(Base.iterate, Base.promote_typeof, X)
│   %2 = X
│   %3 = Core.tuple(T)
│   %4 = Core._apply_iterate(Base.iterate, Base.getindex, %3, %2)
└──      return %4
)

Note that it is basically Base.promote_typeof(1, 1.0, 1f0)[1, 1.0, 1f0], i.e. Float64[1, 1.0, 1f0] thanks to that promote_typeof.

4 Likes

See also

julia> Meta.@lower [1, 1.0, 1f0]
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Base.vect(1, 1.0, 1.0f0)
└──      return %1
))))

help?> Base.vect
  vect(X...)

  Create a Vector with element type computed from the promote_typeof of the argument, containing the argument list.

Related to this: