-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
using 'yield' automatically turns functions into generators #3240
Conversation
I have not included tests on purpose. Generators not yet widely supported so it would break tests for too many people. At the same time, the new functionality is clean, simple and allows node.js developers like me to code very fast and memory-efficient callback solutions (see bluebird) |
Just to illustrate the point of why this is awesome, using bluebird and this pull request you can write the below code. Super simple to read, and supports the tried & true try, catch, finally for error handling.
|
@almost — What do you think about how this PR does things? |
Looks nice, the improvements to the CoffeeScript code recently allow a nice and neat implementation :) I think yieldfrom is pretty useful to have too, but this is the main thing. |
@alubbe I'm not sure I get that example, you're yielding the result of delay called with a single argument (500) so func in delay is going to be undefined, right? |
My bad, wrong piece of code. The function I meant is
|
Oh cool, that makes sense! |
@jashkenas |
I dig it. We should probably find a good way to introduce it. |
I agree. What do you have in mind/where can I help? |
You could add compilation tests? |
So I was originally opposed to unit tests because they would break the existing test suite for everyone who is not using node 0.11 and the --harmony or --harmony-generators tags. I have come up with a solution where the generators test checks each passed argument in process.execArgv and only executes if it finds harmony somewhere in there. To actually execute it, you can start pass the harmony argument to the test suite by using npm run-script test-harmony. |
return 1 | ||
return 0 | ||
|
||
if generatorsAreAvailable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# ensure that these tests are only run if generators are available
generatorsAreAvailable = ->
for execArg in process.execArgv when execArg is '--harmony'
return yes
no
return unless generatorsAreAvailable()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, please do not use top level returns. While node accepts it, it is invalid JavaScript and static analysis tools will reject it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, I was mostly concerned with the 1 and 0 :)
Okay, I cleaned up the test suite so that the generator test file does not even get loaded when generators are unavailable. This avoids any kind of runtime errors that the Let me know what you guys think. |
Honestly I'd prefer the previous version, now if I go and change the file and don't see an error it is not obvious why it is not getting run, I'd keep it in that file until we can remove it. No harm in having that |
Actually, nodejs will pre-load the test, even beyond the I'll modify the code to avoid double-negatives. |
# Ignore generators test file if generators are not available | ||
generatorsAreAvailable = no | ||
for execArg in process.execArgv when execArg in ['--harmony', '--harmony-generators'] | ||
generatorsAreAvailable = yes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another opinion on the readability of this bit of code: wouldn't this be the same as:
generatorsAvailable = '--harmony' in process.execArgv or
'--harmony-generaors' in process.execArgv
# Or...
generatorsAvailable = process.execArgv.some (arg) ->
arg in ['--harmony', '--harmony-generators']
?
I find both of them more straightforward to read. But, i usually prefer single assignment variables if possible, so it probably is a very biased opinion =P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I'd expect break
after this line. Is execArgv.some
guaranteed to be defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is execArgv.some guaranteed to be defined?
IDK if the compiler is supposed to run on old IE (if it is: does someone test that?), but, in case it is, there seems to be a helper for that available.
I guess it should work on oldIE, yes. sigh I'll just go ahead and bet .some wasn't there before IE9 ... |
Surely we don't run the Cakefile in IE? |
@geoffreak you got it, that's how it works now! do co ->
yield something()
co do ->
yield something() compiles to this co(function*() {
return (yield something());
})();
co((function*() {
return (yield something());
})()); So if I understand you correctly, you want to be using the |
@alubbe co(->*
yield something()
)() |
Yep, and now it works with generators - in both of the above examples it generated |
Rad! Here we go. |
using 'yield' automatically turns functions into generators
@alubbe @jashkenas Thank you!! |
Yay! Finally 🎉 |
Score!!! Thank you, thank you, thank you @alubbe @jashkenas. |
This just made my day. @alubbe do you have a gittip or equivalent? |
The test case that's in this patch is very nice — but if anyone wants to beef it up a little bit ... with examples that show yield operator precedence, yield detection inside of nested stuff inside the function, and maybe a trickier case of yield/yieldfrom — that would make me feel a little safer. Also — if anyone has any concerns about the final state of the merged patch... |
Nice :-). |
|
nice 👍 |
Finally 👍 |
great ! |
I'm ecstatic! Now, uh, what version number should the release that contains this be? :) |
nice!! |
@jashkenas how about 1.8.1 release? |
my understanding of semver is that 1.8.1 would be a bugfix to 1.8.0, 1.9.0 would be new features that are backwards compatible and 2.0.0 would be new features that are backwards-incompatible. Seeing as coffeescript always reserved the yield keyword, this PR adds new features that are 100% backwards compatible, therefore it would make sense to go with 1.9.0. |
@SteveEisner @twincharged @matthewwithanm thank you for all the flowers! Happy to see this PR merged and that it will hopefully make everyone's code and life a bit nicer. Also, thanks for offering to tip, but I'd feel better about something non-monetary. If you'd like you can follow me or something O_o'' |
@lazutkin @bjmiller The version should at least be 1.9.0 according to Semantic Versioning! And as @alubbe says, the yield keyword has been reserved since the first version of CoffeeScript so a major version bump for this feature isn't strictly necessary. |
Not that @jashkenas believes in semver ;). |
@body = body or new Block | ||
@bound = tag is 'boundfunc' | ||
@isGenerator = @body.contains (node) -> | ||
node instanceof Op and node.operator in ['yield', 'yield*'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nowhere near sufficient to detect yield
in the body. This assumes the function has a completely flat structure. f (yield 0), (yield 1)
will fail the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f (yield 0), (yield 1)
fails because yield
is used outside of a function definition. The parser works recursively, so nested structures will indeed pass the test. For example, x = -> f (yield 0), (yield 1)
works correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, @body.contains
walks the tree. I had thought it was shallow.
OMG. Congrats on finally getting this in!! Can't wait for 1.9.0! And phew! I was thinking I would have to abandon the language. So glad to see this, and can't wait to use it in production code with |
So, any plans to support it? I think, there should be some way to explicitly create a generator function. Otherwise, generators support seems incomplete. |
@lydell thanx! |
See discussion #3078