With that said...
> In theory a "sufficiently smart language" should have a feature to understand that `thing[3]` can be translated to "call `next()` 3 times
This might be a newbie trap, because next() isn't the same as indexing. What happens if I perform `thing[7]` followed by `thing[5]`? Should performing `thing[7]` put 1-6 in memory and turn the object into a generator-list hybrid?
You're right here. Probably can't work like that since generators are too general, you can't expect them to be rewindable or to not have side effects... prob you'd need a more specialized concept like a "lazy list" that would be a subtype of generator with some extra restrictions that would make it possible to implement the "hybrid" structure as an implementation detail without changing semantics.
Anyway... it would be too much work and probably would turn into a footgun.
Generators should be treatable as lazy lists in the end, and lists should have a common interface whether they are lazy or eager. Someone should figure out a way to have us write code at a higher abstraction level and have same interface for lazy vs eager data structures.
...but it's not gonna happen in a dynamic language like Python. And I can't say I like the "solution" of having the entire language be lazy like Haskell either :|
We're stuck with "casting generators" for now, I guess, but it really does suck!