# Can \`eltype()\` deduce the element type of a generator?

**URL:** <https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429>\
**Category:** Internals & Design\
**Tags:** data\
**Created:** [October 13, 2017, 8:01pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429 "2017-10-13T20:01:52Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 13, 2017, 8:01pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/1 "2017-10-13T20:01:52Z")

</div>

As of Julia Version 0.7.0-DEV.2036, calling `eltype()` on even the simplest generator returns `Any`:

```julia
julia> eltype(x for x in 1:2)
Any

```

Unless the generator function is very complicated, It does not look like a hard problem to deduce the actual type. As a POC, I tried

```julia
julia> Base.eltype(g::Base.Generator) = Base.return_types(g.f, (eltype(g.iter),))[1]
julia> eltype(x for x in 1:2)
Int64
julia> eltype(x*1.0 for x in 1:2)
Float64

```

I don’t think this would be hard to generalize for the case when `return_types()` returns multiple values.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [October 13, 2017, 8:36pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/2 "2017-10-13T20:36:10Z")

</div>

No you code should not rely on inference.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 13, 2017, 9:01pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/3 "2017-10-13T21:01:39Z")

</div>

> [@yuyichao](#):
>
> code should not rely on inference.

Would you mind elaborating on this some more? Not being able to get the element type of a generator seems to significantly limit generators’ usefulness. Is there some way to decorate a generator with the element type information? For example, I can probably write a macro `@ET` such that `eltype(@ET Int (x for x in 1:2))` would return `Int`.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [October 13, 2017, 9:11pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/4 "2017-10-13T21:11:59Z")

</div>

> [@Alexander\_Belopolsky](#):
>
> Not being able to get the element type of a generator seems to significantly limit generators’ usefulness.

No that’s not supposed to matter at all.

> [@Alexander\_Belopolsky](#):
>
> Is there some way to decorate a generator with the element type information?

No

> [@Alexander\_Belopolsky](#):
>
> For example, I can probably write a macro @ET such that eltype(@ET Int (x for x in 1:2)) would return Int.

You can certainly have your own wrapper to do that.

---

<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:** [October 13, 2017, 9:45pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/5 "2017-10-13T21:45:00Z")

</div>

> [@yuyichao](#):
>
> > Is there some way to decorate a generator with the element type information?
> 
> No

You could totally create a wrapper struct for generators that asserted the type of all elements must be the type of the first (or is `Union{}` if empty). Our iteration protocol makes that a bit tricky at the moment, but it’s totally doable. Grab and store the first element and the iteration state, and then use a “deferred” iteration strategy wherein the subsequent element (or Nullable()) is in the state.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 13, 2017, 9:46pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/6 "2017-10-13T21:46:30Z")

</div>

> [@yuyichao](#):
>
> No that’s not supposed to matter at all.

Is that me being dumb or you being terse? 🙂

I’ve just looked at how `collect(::Generator)` is [implemented](https://github.com/JuliaLang/julia/blob/master/base/array.jl#L650). The first thing it does is inferring the element type:

> <https://github.com/JuliaLang/julia/blob/84ca8eea42a93aa31f8434d7003058099588bd07/base/array.jl#L652>

Where the macro `@default_eltype` expands to a call to `Core.Inference.return_type`.

So why is `collect()` allowed to use inference while `eltype()` is not?

---

<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:** [October 13, 2017, 9:57pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/7 "2017-10-13T21:57:19Z")

</div>

The key is _how_ collect uses that information. It’s only used in the empty case. Otherwise, collect just uses the types of the _values_ that it extracts from the array, gradually widening the array type as needed.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 13, 2017, 10:01pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/8 "2017-10-13T22:01:18Z")

</div>

> [@mbauman](#):
>
> It’s only used in the empty case.

Is that true? It looks like in the `SizeUnknown` branch it is used unconditionally.

> <https://github.com/JuliaLang/julia/blob/84ca8eea42a93aa31f8434d7003058099588bd07/base/array.jl#L654>

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [October 13, 2017, 10:02pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/9 "2017-10-13T22:02:38Z")

</div>

> [@Alexander\_Belopolsky](#):
>
> Is that true? It looks like in the SizeUnknown branch it is used unconditionally.

Have a look at what `grow_to!` does.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [October 13, 2017, 10:04pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/10 "2017-10-13T22:04:31Z")

</div>

> [@Alexander\_Belopolsky](#):
>
> So why is collect() allowed to use inference while eltype() is not?

Because people don’t like predictable behavior and forced the base to use the inference to give non-error in some cases that should really be error.

> [@Alexander\_Belopolsky](#):
>
> It looks like in the SizeUnknown branch it is used unconditionally.

Yes, but it doesn’t/shouldn’t affect the final value. If it does, you should report it as a bug.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 13, 2017, 10:20pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/11 "2017-10-13T22:20:35Z")

</div>

> [@nalimilan](#):
>
> Have a look at what grow\_to! does.

I am new to this community, but you guys seem to like to communicate in riddles.

Why does it matter what exactly `grow_to! ` does when it receives the inferred element type encoded in the type of its first argument? Looking closer at the two-argument `grow_to! `, I do realize that it discards the `dest` type that it receives replacing it with `Union{}`:

> <https://github.com/JuliaLang/julia/blob/84ca8eea42a93aa31f8434d7003058099588bd07/base/array.jl#L710>

What I don’t understand is why this is necessary and if it is why it does not matter. From what I see `grow_to!` has to do a lot of extra work because it does not know the element type of the result.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 13, 2017, 10:25pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/12 "2017-10-13T22:25:50Z")

</div>

> [@yuyichao](#):
>
> Because people don’t like predictable behavior and forced the base to use the inference to give non-error in some cases that should really be error.

Is that sarcasm or a serious explanation? Are you suggesting that collecting from an empty generator should give an error?

---

<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:** [October 13, 2017, 10:35pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/13 "2017-10-13T22:35:16Z")

</div>

> [@Alexander\_Belopolsky](#):
>
> you guys seem to like to communicate in riddles

Haha, sorry about that. This one decision has elicited a _lot_ of discussion over the past few years… and you’re in the development channel, which is where you’ll tend to get much shorter and curt replies that assume quite a bit of knowledge.

In short, type inference is entirely an optimization. It should be unobservable. We should be able to turn off inference or improve its results nilly wily without any _results_ changing. Performance will change, of course.

The return type of comprehensions used to depend on inference in _all cases_, and it could cause lots of surprising effects. If you changed something to be a global that wrecked type inference, you’d suddenly get an `Array{Any}` that can’t work with BLAS or other external (non-Julia) libraries.

Yes, there are three options for what to do in the `[x for x in empty]` case — use inference, return an `Array{Union{}}` or be an error. All three have been proposed, and I think we did the `Union{}` thing for a brief while.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [October 13, 2017, 10:37pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/14 "2017-10-13T22:37:36Z")

</div>

> [@Alexander\_Belopolsky](#):
>
> Is that sarcasm or a serious explanation? Are you suggesting that collecting from an empty generator should give an error?

That is a serious explanation and yes I am saying that an error is better than a random type. A `Vector{Union{}}(0)` would work too of course though it’ll cause type instability.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 13, 2017, 11:24pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/15 "2017-10-13T23:24:11Z")

</div>

> [@mbauman](#):
>
> you’re in the development channel, which is where you’ll tend to get much shorter and curt replies that assume quite a bit of knowledge.

Thanks, @mbauman. This makes perfect sense. And thank you for being patient with me. I should have reformulated my question as a user question and posted it under “Usage”. This said, what is the proper etiquette here - should I open a new topic or change the category here?

My specific question relates to implementing an [IterableTables](https://github.com/davidanthoff/IterableTables.jl) “sink” functionality for my own table type. The approach taken by IterableTables is to wrap generators in an element type aware “GeneratorIterator”:

```julia
julia> eltype(TableTraits.getiterator(i for i in 1:0))
Int64
julia> eltype(TableTraits.getiterator(@NT(a=i, b=0.1i) for i in 1:0))
NamedTuples._NT_a_b{Int64,Float64}

```

The discussion in this topic so far suggests that this is not a good idea. What would be the right approach? I don’t think throwing an error on an empty query result is an option in my case.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [October 13, 2017, 11:56pm UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/16 "2017-10-13T23:56:59Z")

</div>

That’s exactly why I said,

> [@yuyichao](#):
>
> No that’s not supposed to matter at all.

If the user of a iterator wants a type when it’s not empty, that type should be supplied explicitly. Otherwise no answer can be given theoretically. In general though the user should not care about the type at all and just passing on the iterator does not require any knowledge of the return type of the iterator.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 14, 2017, 12:31am UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/17 "2017-10-14T00:31:23Z")

</div>

> [@yuyichao](#):
>
> just passing on the iterator does not require any knowledge of the return type of the iterator.

That’s true, but the problem that I am trying to solve does seem to require such knowledge. Moreover, all solutions I’ve seen so far (`Base.collect()` and `TableTraits.getiterator()`) do use inference in one way or another. I understand that inference may at times be fragile, but it is not in the case of `f(x)::T for x in iter`. A macro can easily be written to convert a generator expression with a type assert to an iterator type with `iteratoreltype(iter) == HasEltype()`. Why couldn’t that be the default?

The current behavior –

```julia
julia> eltype(x::Int for x in 1:0)
Any

```

– looks very counterintuitive.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [October 14, 2017, 1:34am UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/18 "2017-10-14T01:34:31Z")

</div>

> [@Alexander\_Belopolsky](#):
>
> but it is not in the case of f(x)::T for x in iter

It is, `T` is not necessarily known and that it is still meaningless in the empty case as well as being able to be `Union{}` instead.  
It’s also wrong when `T` is not a leaftype.

> [@Alexander\_Belopolsky](#):
>
> Why couldn’t that be the default?

Because it couldn’t be done as explained above.

And as I said, if you know the type, supply it explicitly to the user of the iterator. Having a eltype is not what a generator is for.

---

<div class="post-metadata">

**Author:** ![Alexander\_Belopolsky](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander_belopolsky/32/4882_2.png) [@Alexander\_Belopolsky](https://discourse.julialang.org/u/Alexander_Belopolsky)\
**Post date:** [October 14, 2017, 2:32am UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/19 "2017-10-14T02:32:12Z")

</div>

> [@yuyichao](#):
>
> Because it couldn’t be done as explained above.

It was _stated_ that it could not be done, but not explained. Having `eltype()` of a generator return the actual type _can_ be done as I have shown in my first post. My solution may violate some theoretical principles, but in practice, the same solution is implemented in various places. @mbauman explained that type inference may be fragile and should be avoided, but what can be wrong with the following POC:

```julia
struct TypedGenerator{T}
    g::Base.Generator
end

```

```julia
macro typed(ex)
    if ex.args[1].head == :(::)
        :(TypedGenerator{$(ex.args[1].args[2])}($ex))
    else
        ex
    end
end

```

```julia
Base.iteratoreltype(::TypedGenerator{T}) where T = Base.HasEltype()
Base.eltype(::TypedGenerator{T}) where T = T
# Define iterator protocol methods delegating to `.g`.

```

```julia
julia> eltype(@typed (f(x) for x in 1:2))
Any

julia> eltype(@typed (f(x)::Int for x in 1:2))
Int64

```

If such code is acceptable in a user program, why would it be a bad idea for `(f(x)::Int for x in 1:2)` to return a variant of `TypedGenerator{T}` without the `@typed` macro?

> [@yuyichao](#):
>
> If the user of a iterator wants a type when it’s ~~not~~ empty, that type should be supplied explicitly.

How would the user that knows the type of `f(x)` and wants to provide that type in a generator expression ` (f(x) for x in iter)` actually do it? I suggest that ` (f(x)::T for x in iter)` is the first thing that comes to mind and if you subscribe to the principle of least surprise, the `eltype()` of this expression should be `T`.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [October 14, 2017, 2:47am UTC](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429/20 "2017-10-14T02:47:05Z")

</div>

> [@Alexander\_Belopolsky](#):
>
> but not explained

I did!

> [@yuyichao](#):
>
> It is, T is not necessarily known and that it is still meaningless in the empty case as well as being able to be Union{} instead.
> 
> It’s also wrong when T is not a leaftype.

> [@Alexander\_Belopolsky](#):
>
> but what can be wrong with the following POC:

So to repeat,

Or to break it down,

1. T is not necessarily known (and it might not be a constant at all)
2. It is still meaningless in the empty case
3. Return value could be `Union{}`
4. T may not be a leaftype.

> [@Alexander\_Belopolsky](#):
>
> why would it be a bad idea for (f(x)::Int for x in 1:2) to return a variant of TypedGenerator{T} without the @typed macro?

Again, because it’s wrong.

> [@Alexander\_Belopolsky](#):
>
> How would a user that knows the type of f(x) and and wants to provide that type in a generator expression (f(x) for x in iter) actually do it?

Don’t use generator and use some other type. Any wrapper around this iterator would do. For example, just give the type to your `TableTraits.getiterator` function.

> [@Alexander\_Belopolsky](#):
>
> I suggest that (f(x)::T for x in iter) is the first thing that comes to mind and if you subscribe to the principle of least surprise, the eltype() of this expression should be T.

Adding a `::T` in an expression to change the behavior is about as much surprise as you can get. This kind of behavioral change does not exist anywhere else in the language.

[Next page](https://discourse.julialang.org/t/can-eltype-deduce-the-element-type-of-a-generator/6429.md?page=2)
