Generators Are Like Arrays
gist.github.com
gist.github.com
It was six men of Indostan To learning much inclined, Who went to see the Elephant (Though all of them were blind), That each by observation Might satisfy his mind.
The First approached the Elephant, And happening to fall Against his broad and sturdy side, At once began to bawl: "God bless me! but the Elephant Is very like a WALL!"
The Second, feeling of the tusk, Cried, "Ho, what have we here, So very round and smooth and sharp? To me 'tis mighty clear This wonder of an Elephant Is very like a SPEAR!"
The Third approached the animal, And happening to take The squirming trunk within his hands, Thus boldly up and spake: "I see," quoth he, "the Elephant Is very like a SNAKE!"
The Fourth reached out an eager hand, And felt about the knee "What most this wondrous beast is like Is mighty plain," quoth he: "'Tis clear enough the Elephant Is very like a TREE!"
The Fifth, who chanced to touch the ear, Said: "E'en the blindest man Can tell what this resembles most; Deny the fact who can, This marvel of an Elephant Is very like a FAN!"
The Sixth no sooner had begun About the beast to grope, Than seizing on the swinging tail That fell within his scope, "I see," quoth he, "the Elephant Is very like a ROPE!"
And so these men of Indostan Disputed loud and long, Each in his own opinion Exceeding stiff and strong, Though each was partly in the right, And all were in the wrong!
They're nothing like arrays, except that you can iterate over them. That is, they're no more like arrays than trees, lists, and other iterable data structures are like arrays, which is to say, not particularly.
But you can't implement a promise using an array. The array will never "magically" come to life a couple of seconds after you last touched it and do something. And that's the whole point of the gist.
Do you also consider functions to be syntactic sugar? Because you can also implement them in terms of a switch statement.
The reason generators & coroutines are popular is because they allow programmers to use the same user-interface they're familiar with - namely, statements separated by semicolons, all sharing a common lexical environment - to reason about async code that may yield back to an event loop. Promises and callbacks do not have this property: they force the programmer to make the continuation explicit. While under the hood a generator is just an array of thunks and a common environment, the whole point of a high-level language is to hide what's under the hood.
This is exactly what I'm complaining about in the gist... :(
Generators != coroutines. You can use generators to simulate coroutines - but only if you combine them with either Promises or callbacks (or another async abstraction). Pretending like generators "replace" promises or callbacks doesn't help anybody imo. ES7 async functions are a great example of what happens when you embrace the fact that generators need promises of some sort to act as coroutines. You gain return values of functions - something that kinda got lost when JS started using callbacks:
async function getUsername(id) {
let user = await getUser(id);
return user.name; // returns a Promise<String>
});
By pretending that generator-based coroutines "got rid of" callbacks and promises, we are taking a huge step back. Even co is by now embracing the fact that coroutines require more than "just" generators.*(*) Unfortunate only that koa gained traction before co got that memo. Otherwise it might ended up with a saner API than "mutate random properties on this". Maybe even something crazy/revolutionary like "return a response".
I do think that combining futures/promises with generators gives a much cleaner programming model. Take a look at how Python 3.4 handles generators & async programming; here a generator is implicitly a Future, and 'yield from' runs a future to completion, letting you compose coroutines regardless of how many yield points they have.
But my point is that these are all programming models, different ways to present the underlying abstraction to programmers. It's silly to talk about "the fact that coroutines require..." or "generators need promises" or "pretending that...", as if there were only one way to do async programming - these are not universal abstractions, they are models to present the underlying scheduler to programmers, and we choose between them based on how convenient they are to programmers.
"Being like an array" and "can be stored in an array" seems to be two different things.
It's this extra flexibility in control flow that lets you write nicer asynchronous code.
There's no way to use just generators and nothing else and get async control flow going. That's not how generators work. And `yield` is not "about control flow". It's about lazy sequences. And one of the many possible ways to use lazy sequences is to model control flow, when combined with a control flow library.
Especially if you were to look around at other languages' implementations of generators :)
Saying that "yield is not about control flow, it's about lazy sequences" seems like saying "functions are not about control flow, they are about producing values".
Yes, and that bucket is called a Stream, as used effectively by FRP libs like RxJS and Bacon.js, and by Highland (http://highlandjs.org/, made by the guy who made async).
I agree with the gist of the article though. Programmable lazy lists don't do anything special to solve the async problem unless they're programmed to.
Generators aren't a data structure - or if they are, then functions are a data structure too. This isn't array, this is function:
function doStuff() {
return [
fs.readFile.bind(null, 'hello.txt'),
fs.readFile.bind(null, 'world.txt'),
fs.readFile.bind(null, 'and-such.txt')
];
}However I give it to them it's not the whole story, and things like promises/callbacks are needed for async behavior.
Now, you could have an async yield.