Anonymous Generators ?

# Hemanth H.M (12 years ago)
function* FileReader(){
    // Some stream code on node;
    stream.on('data',function*(data) {
          yield data;
    });
}

Now :

var reader = FileReader();
console.log(reader.next()); // Would say { value: undefined, done: true }

The question being, where will the anonymous function yield the results to ?

P.S : This might be an IRC question, but wanted dig bit deeper than just solving this issue.

# Brendan Eich (12 years ago)

The FileReader generator is yield-free. Seems like a bug in that code.

# Hemanth H.M (12 years ago)

Yes, that's where I'm stuck.

Here are the few variations I tired :

function* FileReader(){
    // Some stream code on node;
    stream.on('data',yield function*(data) {
          yield data;
    });
}

function* FileReader(){
    // Some stream code on node;
    yield stream.on('data',function*(data) {
          yield data;
    });
}

function* FileReader(){
    // Some stream code on node;
   var res = ' ';
    stream.on('data',function(data) {
          res = data;
    });
    yield res;
}

Creating an iterator for the parent generators will not help to iterator over the nested/child anonymous generators?

# Brendan Eich (12 years ago)

Hemanth H.M wrote:

Yes, that's where I'm stuck.

Here are the few variations I tired :

function* FileReader(){
    // Some stream code on node;
    stream.on('data',yield function*(data) {
          yield data;
    });
}

Please notice that stream.on, or any event emitter.on in Node.js, will set an event listener callback to be called later, the generator function activation in which you call stream.on will have returned already. You want to use something like task.js here. See taskjs.org.

# Hemanth H.M (12 years ago)

Yes, I'm aware of taskjs.

So it's not a good idea to mix streams and generators!

Thanks :)

# Brendan Eich (12 years ago)

Hemanth H.M wrote:

Yes, I'm aware of taskjs.

So it's not a good idea to mix streams and generators!

No, it's important not to think yield from a generator function nested in another will yield from the outer one. That seems to be what you thought, in all the variations.

Passing a function as a "downward funarg", whether generator or not, means the callee or whatever code eventually does something with that funarg may not call it before control returns to the outer function's activation -- and returns from there.

Thinking about concurrency requires more than generators. Generators return iterators which can be used synchronously, or not. If you write async patterns, including callbacks or event listeners set by .on(), it's up to you to schedule things. This is not a fault of generators, or of streams (or of event emitters in general).

# Hemanth H.M (12 years ago)

I was absolutely under that assumption.

Streams allow the event loop to run between iterations, unlike yield, that's where it gets tricky and converting a stream based logic to task.js isn't straight forward.

# Brendan Eich (12 years ago)

Hemanth H.M wrote:

Streams allow the event loop to run between iterations, unlike yield, that's where it gets tricky and converting a stream based logic to task.js isn't straight forward.

How about something like this?

spawn(function *() {
     var gen = this.thread;

     stream.on('data', function (data) {
           gen.send(data);
     });

     console.log(yield gen.next());
});
# Brendan Eich (12 years ago)

Brendan Eich wrote:

How about something like this?

spawn(function *() {
    var gen = this.thread;

    stream.on('data', function (data) {
          gen.send(data);
    });

    console.log(yield gen.next());
});

Sorry, a generator instance can't next itself, so that last statement should be:

console.log(yield undefined);

We just need to park the task before console.log is invoked, such that the data the listener receives is sent to the paused task's generator and becomes theactual argument to console.log.

# Hemanth H.M (12 years ago)

Ah! Sweet :)

Makes sense of why send() method was implemented for generators.

Can this be a pattern in itself or is there any specific name for this paradigm ?

# Brendan Eich (12 years ago)

Hemanth H.M wrote:

Can this be a pattern in itself or is there any specific name for this paradigm ?

Python folks call it "coroutines" but it's not the canonical meaning of that word.

Having yield as an expression form rocks -- you can yield a value and then someone can send you the value of that yield expression.

# Hemanth H.M (12 years ago)

Thank you, for making it clear :-)

# Brendan Eich (12 years ago)

Brendan Eich wrote:

console.log(yield undefined); 

And in ES6 as amended, this should just be:

 console.log(yield);

Yield (after return, but return is a statement form, of course -- yield is an expression form due to send) can have an operand on its right, or no operand (meaning yield undefined). Cc'ing Allen to double-check that the spec will say so.

# Allen Wirfs-Brock (12 years ago)

On Dec 10, 2013, at 10:18 AM, Brendan Eich wrote:

Yield (after return, but return is a statement form, of course -- yield is an expression form due to send) can have an operand on its right, or no operand (meaning yield undefined). Cc'ing Allen to double-check that the spec will say so.

Already says it...

# Rick Waldron (12 years ago)

On Sunday, December 8, 2013, Hemanth H.M wrote:

Ah! Sweet :)

Makes sense of why send() method was implemented for generators.

send(value) was removed in favor of next(value)

# Brendan Eich (12 years ago)

Rick Waldron wrote:

send(value) was removed in favor of next(value)

Thanks for reminding me -- the SpiderMonkey (after Python) split of send from next reflects the evolution in Python of yield from a statement form into an expression form (to which one sends values), combined with Python's strict arity checking (next takes 0 args, send takes 1). At least, that's my understanding.

ES6 has no reason not to unify send with next, so we did -- along with axing StopIteration in favor of a functional (record returning) style, which engines will have to optimize (not hard, but nothing's free) to avoid allocation per user-coded iterator next().