Retrieving generator references
It just goes to show how good promises are for this kind of problem. I’d probably promisify readFile
and use Q.spawn()
.
The best I could come up with for Node.js-style callbacks was the following code.
var fs = require("fs");
function *init() {
var nextFunc = yield;
var contents = yield fs.readFile("config.json", nextFunc);
doSomethingWith(contents);
console.log("Done");
}
var task = init();
task.next();
task.next(nextify(task));
function nextify(myTask) {
return function(error, result) {
if (error) {
myTask.throw(error);
} else {
myTask.next(result);
}
};
}
I can't go into more detail at the moment, but you might want to check out task.js (taskjs.org) and the async function proposal for ES7 ( lukehoban/ecmascript-asyncawait).
As an aside, I still feel that two concerns are mixed in a generator function:
- The creation of the generator object. This is where the generator function is like a constructor and where you’d expect
this
to refer to the generator object. - The behavior. This is where the generator function is like a real function.
A result of this mixing of concerns is that using next()
to start a generator feels slightly off and that the argument of that first next()
invocation is completely ignored.
Alas, I have no idea how to disentangle these concerns, but it would be nice if we were able to.
Axel Rauschmayer wrote:
As an aside, I still feel that two concerns are mixed in a generator function:
- The creation of the generator object. This is where the generator function is like a constructor and where you’d expect
this
to refer to the generator object.
Nope, not a constructor (and if you don't call with 'new', what is 'this', pray tell?).
- The behavior. This is where the generator function is like a real function.
A result of this mixing of concerns is that using
next()
to start a generator feels slightly off and that the argument of that firstnext()
invocation is completely ignored.
We've discussed a special generator head form to declare that name, but not for ES6. For now it's Pythonic, except we decided not to throw if the initial .next call passes a non-undefined value.
Alas, I have no idea how to disentangle these concerns, but it would be nice if we were able to.
We're not changing generators at this point, but Nicholas's request for a way to reference the running generator-iterator instance is worth discussing more. It can't and shouldn't be 'this'. Does it violate POLA to provide (via another special form) for all generators? Users can provide their own bindings as noted, so is it a big problem?
I think the task.js reference, and ES7 async/await, point to a better direction: use helpers (libraries now, syntax soon) to automate harder and avoid the need for the "me" reference.
- The creation of the generator object. This is where the generator function is like a constructor and where you’d expect
this
to refer to the generator object.Nope, not a constructor (and if you don't call with 'new', what is 'this', pray tell?).
I only mean it is “like a constructor” in that it is a function that, when invoked (in some way), returns a fresh object.
The main purpose of a generator function is to specify the behavior of the generator object. But it also serves as its constructing entity. A double duty.
- The behavior. This is where the generator function is like a real function.
A result of this mixing of concerns is that using
next()
to start a generator feels slightly off and that the argument of that firstnext()
invocation is completely ignored.We've discussed a special generator head form to declare that name, but not for ES6. For now it's Pythonic, except we decided not to throw if the initial .next call passes a non-undefined value.
Alas, I have no idea how to disentangle these concerns, but it would be nice if we were able to.
We're not changing generators at this point, but Nicholas's request for a way to reference the running generator-iterator instance is worth discussing more. It can't and shouldn't be 'this'. Does it violate POLA to provide (via another special form) for all generators? Users can provide their own bindings as noted, so is it a big problem?
I’d pass the generator object reference to the generator function via next()
, as the first step after creating the generator object. Not pretty, but relatively clean.
I think the task.js reference, and ES7 async/await, point to a better direction: use helpers (libraries now, syntax soon) to automate harder and avoid the need for the "me" reference.
I agree, I can’t think of a solution that wouldn’t be much too complicated for the minor problem that it solves.
On Sat, Nov 22, 2014 at 8:03 PM, Brendan Eich <brendan at mozilla.org> wrote:
Axel Rauschmayer wrote:
A result of this mixing of concerns is that using
next()
to start a generator feels slightly off and that the argument of that firstnext()
invocation is completely ignored.We've discussed a special generator head form to declare that name, but not for ES6. For now it's Pythonic, except we decided not to throw if the initial .next call passes a non-undefined value.
Yeah, in Python world (where initial JS generators came from) a technique to "prime" a generator is often done via a decorator. JS doesn't have decorators (yet), but it looks like:
// A helper decorator that primes the gen.
function coroutine(func) {
return (...args) => {
var gen;
gen = func(...args);
gen.next();
return gen;
};
}
// Actual gen.
let grep = coroutine(pattern => {
let line;
console.log('Looking for ' + pattern);
while (true) {
line = yield;
if (line.indexOf(pattern !== -1)) {
console.log('Found ' + pattern + ' in ' + line);
}
}
return true;
});
// Usage.
let g = grep('Coffee');
g.send('JavaScript');
g.send('CoffeeScript');
In Python it would be used as:
@coroutine
def grep(pattern):
...
Dmitry
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20141123/bbfc41af/attachment
Thanks all. My intent wasn't to propose that something was necessary, just to ask if something was available that I was missing. I'll dig in on the await spec and take a good look at the various examples everyone provided.
On Sun 23 Nov 2014 12:54, Alex Kocharin <alex at kocharin.ru> writes:
Hmm... Interestingly, in Chrome if you do call it with 'new', 'this' would refer to a generator itself. But not in FireFox. I'm playing around with this example:
function *G() { console.log(this === x) yield 1 console.log(this === x) } x = new G() x.next() x.next()
Shows "true, true" for V8 and "false, false" for FF engine.
This is a bug in SM: bugzilla.mozilla.org/show_bug.cgi?id=907742
Andy
After playing around with generators for most of the day (and pretty much loving all of it), I ended up with a code example for async that looks like this:
The thing that immediately jumped out at me was how poorly the
task
variable is being managed. So I was poking around trying to see if there was a way to get a reference to the generator instance from within the generator itself so I could pass it around, such as:Of course,
this
isn't a reference to the generator itself, but rather the this-binding for the function. It struck me, though, that my example could be a lot cleaner if I could get a reference to the generator from the generator function and pass that around rather than having to manage that reference outside.Now to my question: is there a way to get a reference to the generator from inside the generator function?
(Also, sorry if I'm getting the nomenclature wrong, still trying to wrap my head around the relationship between generators, iterators, and functions.)