I’m used to writing tuple comprehensions using tuple((blah(x) for x in tup)...), but I just found out that it’s not type-stable:
add2(tup) = tuple((x+2 for x in tup)...)
@code_warntype add2((1,2.0,3)) # ::Tuple{Vararg{Union{Float64, Int64},N} where N}
In contrast, add2(tup) = tuple(map(x->x+2, tup)...) is fine (the tuple() is redundant, I know). Are tuple comprehensions just a bad idea in general, or am I doing it wrong?
julia> add2(x) = x + 2
add2 (generic function with 1 method)
julia> t = (1, 2.0, 3)
(1,2.0,3)
julia> add2.(t)
ERROR: MethodError: Cannot `convert` an object of type Tuple{Int64,Float64,Int64} to an object of type CartesianRange{I<:CartesianIndex}
This may have arisen from a call to the constructor CartesianRange{I<:CartesianIndex}(...),
since type constructors fall back to convert methods.
in CartesianRange{I<:CartesianIndex}(::Tuple{Int64,Float64,Int64}) at ./sysimg.jl:53
in broadcast_t(::Function, ::Type{Any}, ::Tuple{Int64,Float64,Int64}, ::Vararg{Tuple{Int64,Float64,Int64},N}) at ./broadcast.jl:214
in broadcast(::Function, ::Tuple{Int64,Float64,Int64}) at ./broadcast.jl:230
The problem is that none of these generator solutions can be type-stable on a heterogeneous input tuple. I was hoping the compiler would automatically unroll (x+2 for x in tup)..., but I suppose the map/broadcast solutions are sufficient.