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

julia> π
π = 3.1415926535897...

julia> π::Int
ERROR: TypeError: typeassert: expected Int64, got Irrational{:π}

Surprise!

That’s not at all what I’m saying! (but yeah, bringing that as an example is quite surprising)

Each expression has it’s semantics and just as you wouldn’t expect f(a) for arbitrary a to not call f, a::T will always do type assertion as that’s what its defined meaning is.
What I am saying though, is that there mustn’t be any non-local behavior change to expressions around it or basically anthing that wouldn’t have happened if this is added anywhere else. The expression gives you an output (error included) from an input. NOTHING more.

OK, what about this:

julia> using IterableTables

julia> eltype(TableTraits.getiterator(x::Float64 for x in []))
Float64

julia> eltype(TableTraits.getiterator(x::Int for x in []))
Int64

Is this another example of people not liking predictable behavior?

Yes, because this appearing is arbitrary context will not work. And to repeat what I said, again,

  1. (error()::Int for x in [])
  2. (x::Integer for x in [1])
  3. (2.3::Int for x in [])
  4. T=Int; ((T=Float64; 1.0)::T for x in [1])

Let’s see

julia> eltype(@typed (error()::Int for x in []))  # 1
Int64

julia> eltype(@typed (x::Integer for x in [1])) # 2
Integer

julia> eltype(@typed (2.3::Int for x in [])) # 3
Int64

julia> T=Int; eltype(@typed ((T=Float64; 1.0)::T for x in [1])) # 4
ERROR: UndefVarError: #4#T not defined

I don’t see why having an abstract type as in (2) be a problem given

julia> collect(Integer, [2.0, 3])
2-element Array{Integer,1}:
 2
 3

The error in (4) was not planned for, but does seem like the right thing to do.

Wrong, should be Union{}

Wrong, should be Int

Wrong, should be Union{}

Scoping issue, put all in a function.

1 Like

And this is exactly what I said you should do instead of what your want your macro to guess, i.e.

Actually I should say more precisely that having your macro to guess is fine, having Base to guess is not. (other than some hygene issue in your macro).
Both because what you have here is NOT a correct answer to the real eltype of the generator by any mean (correct answer given above, which is generally uncomputable without running the generator) and because the syntax is very surprising (explained above). However, as I said above, having a generator wrapper type that carries an eltype is perfectly fine (just don’t make it the default generator). Having a macro to provide some syntax sugar for creating that type is also fine since macros are understood to do transformations that otherwise make no sense (i.e. having a macro breaking down expression and give significance to the structure of an expression is OK).

2 Likes

TableTraits.getiterator is not my function. It comes from a package by @davidanthoff. My package has to specialize it to be able to provide a “source” for the Query.jl queries. I cannot change the signature. Moreover, the return type of a query is rarely known in advance, it has to be inferred from the input.

BTW, I’ve noticed that BaseGenerator has a constructor that takes a type instead of a function, but it still returns a “typeless” iterator:

julia> eltype(Base.Generator(Int, 1:5))
Any

What is the intended use of Generator(T, iter)?

In this case the caller should be change to be able to not rely on that.

It’s just a implementation detail due to how types are dispatched. There’s no significant when the callee is a type.

Sorry for kick-up this thread, but asserting the [concrete] eltype of a generator seems like a great feature, which could be implemented in a package. I realize the iteration protocol has changed, likely since this thread was active. Is there still any need to treat the first call of iterate specially?