-
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
Implement void operator #3731
Implement void operator #3731
Conversation
expr = @expression || new Literal '0' | ||
[ | ||
@makeCode("void(") | ||
expr.compileToFragments(o, LEVEL_LIST)... |
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 don't know if using the LEVEL_LIST here is OK. Basically i tried with different values until void if a then b else c
compiled to void(a ? b : c)
instead of void if (a) { b } else { c }
or void((a ? b : c))
. Does this make sense?
Something similar was proposed for coco: satyr/coco#218. What's wrong with a ml-style
(Well, I guess it's gonna bite you if you try to use spaced-dot and stuff like that... precedence...) |
Compared with this It's funny however that the first basic implementation of |
What do you mean? ignore = ->
parse = (comments) ->
for comment in comments
send comment
ignore() # right, need to call ignore not to be truthy
# ^ did not generate loop values Let's try a benchmark: http://jsperf.com/void-vs-ignore-vs-noreturn |
ignore (for comment in comments then send comment) as compared to: void for comment in comments then send comment If you're going to end your function body with |
Yes, I mentioned the issue of precedence. The "only" advantage ignore has is that you can still put stuff "on the same line" |
I quite like this idea from the perspective of writing code, though I don't think it gives us anything that |
I can't see introducing this feature just to avoid writing |
@michaelficarra —quite right. Adding a void operator is a neat idea to "void-out" the value of an expression, in general ... but it compares quite unfavorably for the proposed use case.
Isn't nearly as nice as:
So — without any good reason for wanting to "void" the value of an expression in general — the value is almost always a useful thing — let's not do this. |
What about moving the nextState = off
$('#btn').click void ->
$('#state').text if nextState then "On" else "Off"
nextState = !nextState |
This one proposal has the same problems as exposed in every previous issue about this. |
Ok, i tried 😛
How about func = ->
void send item for item in list (This is actually the way it's implemented in this PR, as i wanted to support the second example shown in the description.)
That has basically the same problems of using a special function symbol |
Right, but it doesn't introduce a new symbol with its endless permutations (as you pointed out). The void operator is simply an instruction to the compiler. |
I still see it as a permutation, though. The way it's implemented on coco, it's just the "not" operator, not something else ( |
Alas... I wanted a clean |
While this PR might seem like an uncalled attempt of bringing a weird operator from JavaScript nobody seems to miss back to CoffeeScript, it's actually a little stab at trying to solve #2477, i.e., "Remove implicit returns?".
But without removing implicit returns 😺
tl;dr
void
allows fixing the problems cited by @Zyphrax in an arguably more elegant way than addingreturn
s everywhere. For example, this tricky jQuery event handler problem (source) can be fixed by "voiding" the last boolean assignment:↓
Or, if you want to avoid creating a big array because the last expression of your function is a
for
comprehension,void
can save you there too!↓
This way,
void
retains basically the same meaning from JS: it evaluates the expression we pass it and returnsundefined
, with the added benefit of the compiler being able to avoid generating areturn
statement ifvoid
is the last expression of a block because it knows about its semantics.But why not a different function symbol?
I actually was in favour of using
!->
or-/>
to denote procedures. But it has quite a nasty problem: the combinatorial complexity of having various "modifier" behaviours for functions. Now we have only one modifier, whether the function is "bound" or not, so we have two symbols:->
and=>
. Adding an extra modifier, whether it returns useful values or not, would require having four symbols:->
,=>
,!->
and!=>
. Now image if we decided to implement generators as yet another modification of the function syntax... yes, eight(!!!) symbols:->
,=>
,!->
,!=>
,->*
,=>*
,!->*
and!=>*
.It's not only about the grammar complexity, but mostly about the cognitive load of having to pick between many different function types whenever you want to write a damn function. Right now the picking is pretty easy: always pick
->
, and if later on i discover that i need thethis
of the parent context, change it to=>
. Done. And it should stay that simple 😉So, why
void
then?I don't know, it just occurred to me when considering alternative solutions for the sour #2477 issue. At first i thought it was a stupid idea, but after giving it a try it started to make some sense.
First, i think it fits Coffee's philosophy of "everything is an expression" quite nicely.
void <expr>
is itself just an expression, that just happens to always beundefined
.Also, i think that Coffee/Ruby programmers develop a habit of looking at the bottom of blocks to see what they evaluate to. Ideas like the
!->
function syntax or specifying the return value before the function body break that expectation, whilevoid
doesn't.The fact that
void
concept is independent of the "function type" is also a big win. It avoids the possible combinatorial explosion of complexity mentioned above.And it's also worth noting that
void
is already a part of JavaScript; it's not a new concept. It's even a reserved keyword on today's CoffeeScript, so it's not like this change could break code that usesvoid
as an identifier.Sooo... should i then start using void at the end of most of my functions?
Simply put: no! I mean, you can. But it's not in the spirit of this proposal to have
void
as something one would use in the general case of writing procedural functions.For example, it doesn't make sense to have all your test case functions end on
void
because you don't care about the return value. If you don't care, just leave the last expression be returned, and trust that the testing framework won't care about that return value either.void
should be used sparingly, to mark cases where it is important that the function returns undefined:Well, that's all, i hope this proposal can bring an adequate solution for people having trouble with implicit returns, while at the same time not make the language much uglier for people not having problems with them.
What do you people think? 😺