Hi,

On 11/08/2013 15:43, Michael Woerister wrote:
Maybe you do now :)
I see where this is going, that's actually not too bad. So essentially it would generate some anonymous struct and impl.

Can you elaborate on what you mean by "both directions"?
Python has this inheritance tree (virtual):

  Iterable
    Iterator
      Generator

An iterable is defined as a class with a __iter__() that returns an iterator. An iterator is defined as an iterable that returns itself and also has a __next__ method which either returns a value or raises a StopIterator.

A generator is a separate beast that is produced by the compiled if it spots a function with a 'yield' expression. A generator also exposes the iterator protocol but actually does more. If you call __next__() on a generator is equivalent to calling .send(None). Essentially in Python a generator can produce values, but also get data sent:

   def g():
       num = 0
       while 1:
           next_num = (yield num)
           if next_num is not None:
               num = next_num
           else:
               num += 1

If you just iterate over it, it's an eternal iterator that produces ever increasing numbers. If you however call .send(NUM) on it, it sets the internal counter to NUM and yields the same number back next iteration. The ugly side effect of this is that you now need to forward these generators in both directions. PEP 0380 explains how that looks like. eg: for item in x(): yield item is not good enough, as it now throws away the return value from yield.

The reason I'm bringing this up is because in case Rust ever wants to gain support for bidirectional generators it should from the very start have something like a "yield from" to not break the reverse forwarding.

I think Rust has an advantage over Python here, in that for every
function the return type is explicitly declared. So, if a function
returns an Iterator<T> (or better), one does not have to care about
whether the function is implemented via 'yield' or if it returns a
handwritten iterator.
You would think, neither do you in Python. The problem is actually not so much the consuming of the function but the writing of it. In a regular function you have execution as the function is called. In a generator function the execution immediately suspends until you call next(). It's very confusing for beginners and has caused in many, many broken WSGI applications in Python (WSGI is the Python web abstraction protocol that uses iterators).

Even more than that, how do you make an empty generator? There is a lot of code that does any of those:

    def iter_nothing():
        return iter([])

    def iter_nothing():
        if False:
            yield None

Lastly, in Python 3 we now are getting async IO support through generators and the frameworks do weird things to make generator and regular functions decorated with the same decorator, behave similarly in case someone deletes the last 'yield' from the function. An explicit 'yield fn' outside of the function solves this case, where a function degrades to not having any yield expressions any more.

Yes, in theory you could make a function that returns nil but is declared as returning an iterator, just return the empty iterator for that type, but that seems wrong.


Regards,
Armin
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to