# Poor typing of tuple generators

**URL:** https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880
**Category:** General Usage
**Created:** [March 26, 2017, 2:16am UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880 "2017-03-26T02:16:37Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)
#### Post date: [March 26, 2017, 2:16am UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/1 "2017-03-26T02:16:37Z")

</div>

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:

```julia
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?

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [March 26, 2017, 3:46am UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/2 "2017-03-26T03:46:48Z")

</div>

This isn’t a direct answer to your question, but there’s a better option in Julia v0.6:

```julia
julia> add2(x) = x + 2
add2 (generic function with 1 method)

julia> t = (1, 2.0, 3)
(1, 2.0, 3)

julia> add2.(t)
(3, 4.0, 5)

julia> @code_warntype add2.(t)
Variables:
  #self#::Base.#broadcast
  f::#add2
  t::Tuple{Int64,Float64,Int64}
  ts::Tuple{}

Body:
  begin
      return (Core.tuple)((Base.add_int)((Base.getfield)(t::Tuple{Int64,Float64,Int64}, 1)::Int64, 2)::Int64, (Base.add_float)((Base.getfield)(t::Tuple{Int64,Float64,Int64}, 2)::Float64, (Base.sitofp)(Float64, 2)::Float64)::Float64, (Base.add_int)((Base.getfield)(t::Tuple{Int64,Float64,Int64}, 3)::Int64, 2)::Int64)::Tuple{Int64,Float64,Int64}
  end::Tuple{Int64,Float64,Int64}

```

---

<div class="post-metadata">

### Author: ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)
#### Post date: [March 26, 2017, 2:58pm UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/3 "2017-03-26T14:58:07Z")

</div>

> [@rdeits](#):
>
> This isn’t a direct answer to your question, but there’s a better option in Julia v0.6:

.-broadcast exists on v0.5 as well.

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [March 26, 2017, 3:39pm UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/4 "2017-03-26T15:39:48Z")

</div>

True, but it doesn’t handle tuples on v0.5:

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

```

---

<div class="post-metadata">

### Author: ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)
#### Post date: [March 26, 2017, 10:58pm UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/5 "2017-03-26T22:58:10Z")

</div>

Note that it works on Julia 0.5 using `Compat` (see PR [#324](https://github.com/JuliaLang/Compat.jl/pull/324)):

```julia
julia> using Compat

julia> add2(x) = x + 2
add2 (generic function with 1 method)

julia> add2.((1, 2.0, 3))
(3,4.0,5)

```

---

<div class="post-metadata">

### Author: ![jeff.bezanson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeff.bezanson/32/48_2.png) [@jeff.bezanson](https://discourse.julialang.org/u/jeff.bezanson)
#### Post date: [May 8, 2017, 8:44pm UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/6 "2017-05-08T20:44:14Z")

</div>

You can use tuple types as constructors, e.g. `NTuple{4,Int}(2x for x in 1:4)` or `Tuple(2x for x in 1:4)`.

---

<div class="post-metadata">

### Author: ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)
#### Post date: [May 8, 2017, 9:42pm UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/7 "2017-05-08T21:42:55Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [May 8, 2017, 9:58pm UTC](https://discourse.julialang.org/t/poor-typing-of-tuple-generators/2880/8 "2017-05-08T21:58:29Z")

</div>

If you know the function within your generator preserves the type, you can use the type of the original tuple as the constructor:

```julia
julia> add2(tup) = typeof(tup)((x+2 for x in tup)...)
add2 (generic function with 1 method)

julia> @code_warntype add2((1,2.0,3))
Variables:
  #self#::#add2
  tup::Tuple{Int64,Float64,Int64}
  #5::##5#6

Body:
  begin
      SSAValue(0) = Tuple{Int64,Float64,Int64}
      SSAValue(2) = $(Expr(:new, Base.Generator{Tuple{Int64,Float64,Int64},##5#6}, :($(QuoteNode(#5))), :(tup)))
      return (Core._apply)(SSAValue(0), SSAValue(2))::Tuple{Int64,Float64,Int64}
  end::Tuple{Int64,Float64,Int64}

```
