# Can I type assert the eltype of an iterator?

**URL:** <https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936>\
**Category:** General Usage\
**Tags:** question\
**Created:** [July 18, 2017, 7:22pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936 "2017-07-18T19:22:17Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [July 18, 2017, 7:22pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/1 "2017-07-18T19:22:17Z")

</div>

I have a complicated piece of code, with a fragment like:

```julia
prod(i for i in 1:10 if i > 10)

```

The product may or may not be empty, depending on circumstances. When it is empty, Julia gives an error:

> ArgumentError: reducing over an empty collection is not allowed

If I have to check for the emptiness myself it will complicate my code significantly. I would like `prod` to return 1 instead. I think the problem here is that `prod` cannot infer the eltype of the iterator when it is empty.

Is there a way to deal with this?

Oh, this also does not work:

```julia
prod(i::Int for i in 1:10 if i > 10)

```

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [July 18, 2017, 7:31pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/3 "2017-07-18T19:31:26Z")

</div>

Here is a workaround (that I’m not happy with). Define an intermediate function:

```julia
type_empty(itr, T) = isempty(itr) ? T[] : itr 

```

Then you can do:

```julia
prod(type_empty((i::Int for i in 1:10 if i > 10), Int))

```

Or,

```julia
prodt(itr, ::Type{T}) where {T} = isempty(itr) ? one(T) : prod(itr)

```

Is there a better way?

---

<div class="post-metadata">

**Author:** ![anon94023334](https://avatars.discourse-cdn.com/v4/letter/a/e274bd/32.png) [@anon94023334](https://discourse.julialang.org/u/anon94023334)\
**Post date:** [July 18, 2017, 7:55pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/4 "2017-07-18T19:55:04Z")

</div>

If you don’t mind collecting first, you can do  
`prod([i for i=1:10 if i > 10])`

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [July 18, 2017, 8:03pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/5 "2017-07-18T20:03:55Z")

</div>

My iterator is big, so collecting is not an option.

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [July 18, 2017, 8:15pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/6 "2017-07-18T20:15:59Z")

</div>

Now I am getting a different error when I combine iterators:

```julia
prodt((i for i in 1:10 if i > 10 for j in 1:10), Int)

```

> ArgumentError: argument to Flatten must contain at least one iterator

Interestingly, reversing the order of the iterators does not give an error:

```julia
prodt((i for j in 1:10 for i in 1:10 if i > 10), Int)
# runs without problems

```

---

<div class="post-metadata">

**Author:** ![Michael\_Eastwood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michael_eastwood/32/669_2.png) [@Michael\_Eastwood](https://discourse.julialang.org/u/Michael_Eastwood)\
**Post date:** [July 18, 2017, 8:27pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/7 "2017-07-18T20:27:56Z")

</div>

```julia
julia> prodt((i for j in 1:10 for i in 1:10 if i > 10), Int)
1

julia> prodt((i for i in 1:10 if i > 10 for j in 1:10), Int)
ERROR: ArgumentError: argument to Flatten must contain at least one iterator
Stacktrace:
 [1] start(::Base.Iterators.Flatten{Base.Generator{Base.Iterators.Filter{##49#52,UnitRange{Int64}},##47#50}}) at ./iterators.jl:696
 [2] isempty(::Base.Iterators.Flatten{Base.Generator{Base.Iterators.Filter{##49#52,UnitRange{Int64}},##47#50}}) at ./essentials.jl:358
 [3] prodt(::Base.Iterators.Flatten{Base.Generator{Base.Iterators.Filter{##49#52,UnitRange{Int64}},##47#50}}, ::Type{Int64}) at ./REPL[1]:1

julia> prodt((i for i in 1:10 for j in 1:10 if i > 10), Int)
1

```

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [July 18, 2017, 8:35pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/8 "2017-07-18T20:35:47Z")

</div>

Do you know why?

---

<div class="post-metadata">

**Author:** ![ExpandingMan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/expandingman/32/866_2.png) [@ExpandingMan](https://discourse.julialang.org/u/ExpandingMan)\
**Post date:** [July 18, 2017, 8:53pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/9 "2017-07-18T20:53:57Z")

</div>

It seems to me that since indeed the product series of an empty set is not defined, it shouldn’t bother you that you need to check whether the iterator is empty.

Why not simply:

```julia
isempty(iter) ? 1 : prod(iter)

```

where `iter` is your iterator?

---

<div class="post-metadata">

**Author:** ![Michael\_Eastwood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michael_eastwood/32/669_2.png) [@Michael\_Eastwood](https://discourse.julialang.org/u/Michael_Eastwood)\
**Post date:** [July 18, 2017, 9:14pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/10 "2017-07-18T21:14:15Z")

</div>

Same reason as this:

```julia
julia> collect(i for i = 0:-1 for j = 1:1)
ERROR: ArgumentError: argument to Flatten must contain at least one iterator
Stacktrace:
 [1] start(::Base.Iterators.Flatten{Base.Generator{UnitRange{Int64},##97#99}}) at ./iterators.jl:696
 [2] grow_to!(::Array{Int64,1}, ::Base.Iterators.Flatten{Base.Generator{UnitRange{Int64},##97#99}}) at ./array.jl:491
 [3] collect(::Base.Iterators.Flatten{Base.Generator{UnitRange{Int64},##97#99}}) at ./array.jl:397

julia> collect(i for i = 1:1 for j = 0:-1)
0-element Array{Int64,1}

```

Seems like you want your empty generator on the right, so the filter should be attached to the right-most generator if there’s a chance it will be empty. This is entirely based on experimentation though.

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [July 19, 2017, 9:02am UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/11 "2017-07-19T09:02:16Z")

</div>

I have raised an issue about this

> <https://github.com/JuliaLang/julia/issues/22867>
>
> \`\`\`
> prod(\[i for j = 1:10 for i in 1:10 if i \> 10\])
> \# result = 1
> \`\`\`
> 
> but
> 
> …
> \`\`\`
> prod(\[i for i in 1:10 if i \> 10 for j = 1:10\])
> \`\`\`
> \> ERROR: ArgumentError: argument to Flatten must contain at least one iterator
> 
> Something seems wrong here...
> 
> Related: https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936

> [@ExpandingMan](#):
>
> It seems to me that since indeed the product series of an empty set is not defined, it shouldn’t bother you that you need to check whether the iterator is empty.

The product of an empty set is usually taken as 1.

---

<div class="post-metadata">

**Author:** ![ExpandingMan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/expandingman/32/866_2.png) [@ExpandingMan](https://discourse.julialang.org/u/ExpandingMan)\
**Post date:** [July 19, 2017, 1:29pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/12 "2017-07-19T13:29:00Z")

</div>

> [@e3c6](#):
>
> The product of an empty set is usually taken as 1.

I stand [corrected](https://en.wikipedia.org/wiki/Empty_product).

While this limit is far from obvious to me, it seems well motivated (as far as I can tell \Pi(A\cup B) = \Pi(A)\Pi(B) is the best justification) so I’d be in favor of changing `Base` to define `prod` this way. I suppose to be consistent we’d then have to define `sum(T[]) == zero(T)` as well. (I suppose for `Any` it could default to `Int`.)

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [July 19, 2017, 1:40pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/13 "2017-07-19T13:40:41Z")

</div>

It already is:

```julia
julia> sum(Int[])
0

julia> prod(Int[])
1

```

The problem is that with `sum([])` you cannot infer the eltype, so you do not know whether to return `0` (int) or `0.0` (float) or what. It is safer to error.

My point is that `sum` or `prod` could take an extra type argument that enforces an eltype (and errors if it finds an element that does not fit the type), such that the empty iterator could default to `zero(T)` of the specified `T`, in cases where the eltype cannot be infered by the compiler.

---

<div class="post-metadata">

**Author:** ![ExpandingMan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/expandingman/32/866_2.png) [@ExpandingMan](https://discourse.julialang.org/u/ExpandingMan)\
**Post date:** [July 19, 2017, 1:49pm UTC](https://discourse.julialang.org/t/can-i-type-assert-the-eltype-of-an-iterator/4936/14 "2017-07-19T13:49:19Z")

</div>

Oh, sorry, I completely missed what was going on here (and when I tested it I tried `prod([])`.

Now that I see what’s wrong I can give a less idiotic response:

Yes, it seems to me that since indeed the return types of these iterators can be inferred, `Base` should be changed so that the behavior of summing or taking the product series over iterators yields the same bahovior as doing so for arrays.

In your case I would try to ensure that your generators are properly typed, and then do

```julia
prod_proper(iter::Base.Generator{UnitRange{T},U}) where {T,U} = isempty(iter) ? one(T) : prod(iter)

```

though of course whether this is practical depends on how you are creating the generators. In general it’s probably a good practice to have type-stable generators regardless. It’s unfortunate that it’s impossible to overload the method in `Base` without having to re-write the algorithm.
