-
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
Syntax for indicating that a function should not return a value #899
Comments
Nope, Trevor, sorry ... We don't need to introduce a new keyword to accomplish something you can already be doing by adding a CoffeeScript forces you to adopt the habit that you have to think about having a good return value for every function you write -- and I think it's a great habit to have. There's a lot of JS out there that would benefit from better return values, even for side-effect-ful code. Making it easier to create value-less functions is the opposite direction. |
Can we at least use such a syntax to specify a default return value in the signature? So that if return isn't used before the function is ended, then that default value is used. This would be very helpful for functions that creep to the right quite a bit due to many nested functions, where you still want to return some value at the bottom. Currently, you end up created a triangle
Possibly even take some inspiration from google go and be able to define in the signature the variable in the body of the function that should be returned...... :) |
I keep thinking back on this issue as I work, and I'd like to see it reopened. I agree with the philosophy behind implicit return values, and I certainly want to keep them. But the current situation forces CoffeeScript coders unnecessarily to choose between writing clean-looking code and truly efficient code. This isn't an issue in Ruby, partly because bytecode size isn't an issue in Ruby, but mainly because Ruby doesn't have implicit list comprehensions. For instance, the following code looks elegant, but is in fact extremely inefficient—in terms of byte code, performance, and most of all memory—since it creates and returns a deep copy of
Granted, adding a I don't think this has to do with whether programmers have to think about what functions return or not. Whether there's a special syntax for it or not, we're returning the last expression value from a function unless the programmer specifies otherwise. I think delfick's idea of being able to specify a default return value for a function (that is, something that's returned unless the |
Here's another proposal: Let
to
I find the latter both more writable and more readable. I keep hearing complaints about the need to give explicit |
There's been some recent discussion about this on the Google Group. As I've continued to work in CoffeeScript, I've developed a habit of adding |
Continuing with Trevor's comment above, I believe that the additional clarity lent the code written with a *-> or *=> operator is desirable. As a Rubyist, I expect methods to return the last evaluated value. That is not always common Javascript expectation and Coffeescript is, after all, "only Javascript", hence should enable common usage patterns when possible. As a further note, clarity has the benefit of making the code easier to read and less error-prone during a refactor. One cost associated with a language that is whitespace-sensitive is that lining up scopes can be, at times, daunting. Sprinking in explicit returns just for the sake of returning nothing can introduce errors and/or make refactoring difficult. |
The look of |
The syntactic sugar, IMO is secondary to the actual decision to add an operator. However, if it does get down to syntax, I agree that the asterisk connotes less than the word |
Sure, the initially proposed
then the compiler could give you the error |
I agree with satyr. While |
Self-contradiction? (Logging is a side-effect.) |
Oops, typo. Corrected, thanks. Not sure if there's a good term for such functions. "Anti-pure functions"? "Void functions" works well for our purposes. |
@TrevorBurnham: Why would |
@michaelficarra Hmm. But then |
@TrevorBurnham: The meanings would be the same. It is the same operator in both places. Can you outline any differences?
and with arguments:
I still don't even think this is an issue, but at least the |
is very different from the proposed
In addition to DRY concerns, the first approach discards Clarification: The proposed compilation of the given example is
As to "When you don't care about the return value, just forget about it.": I started adding |
Whoops, forgot edit in response to your edit: The compilation can be simplified down when that pattern is recognized, but that's besides the point. Your argument regarding libraries whose behaviour is dependent upon the return value of your function is not very convincing. Either you care about the return value or you do not. And some of those times when you do, that return value is not the value of the last expression of the function body. In libraries like the one you mentioned, you do care about the return value and you do want it to to be different than the last expression. That's the only time the |
@michaelficarra I suppose that adding
you could write
and the compiler, seeing a Note that I'm assuming precedence ordering such that the above would be equivalent to
rather than the less helpful
So, I'm warming to your suggestion. It feels like a good compromise. Essentially,
over
|
After reading this whole thread, I really have to +1 Trevor's points and the proposal for the Personally though, I'll chime in that I'm not a huge fan of automatic/implicit return values for multi-line functions. I get the beauty of not having to write "return foo" for one-liner functions -- just like Python's lambdas -- but in multi-line functions, it's just not obvious when you're reading a function and you don't see the word "return" that a value is getting returned. Because of that, I've tended to generally use the keyword "return" for returning values in non-trivial functions even though I don't need to. And yes, I've had to explicitly return nothing at the end of functions when I mean a function to be void. |
I'd also like to convey my +1 for Trevor's proposal ('void' or '*' or whatever the final syntax is). The main reason is that this makes the code more self-documenting. I really appreciate the implicit return feature of CS. But there will continue to be many cases where return values are explicitly unwanted. In those cases, having the first line of the function definition unambiguously tell you 'this function does not return a value' is very useful. (Even more so when you use an editor that can collapse functions--otherwise you have to uncollapse the function and inspect its code to see whether it has a meaningful return value.) |
Just tripped over a performance issue caused by the implicit return of a list comprehension today, so +1 for this proposal. I would only like to suggest the following shorthand for
I think it looks pretty cool, and it kind of gives the impression that the flow of information is somehow "cut" or "forbidden"
It's only a shorthand though, so it would work the same as using Bound functions could then use the similar |
Wow. I like that syntax a lot. On Jun 26, 2011, at 11:36 AM, [email protected] wrote:
|
Do I understand correctly? The proposal is for this:
instead of already valid:
|
Note in Ruby P.S. Let me add a pseudo example:
|
I dig the appearance of |
@trans Well, with @aseemk I dig the readability and clarity of |
I also like @coreh's propopsed |
It strikes me as too much of a perlism. |
@trans: I still think this discussion is ridiculous. Also:
|
@TrevorBurnham yeah , returns were not the reason for my code failing. It started working shortly after I wrote the above comment. Don't know what was wrong :-x |
I also want a function that doesn't IMPLICITLY return the last statement, which doesn't necessarily mean it always returns undefined even if I EXPLICITLY return something. I would say Having said that, in ES6 spec, there may be a function that always implicitly returns the last statement. When the spec is almost fixed, CoffeeScript could decide to adopt that notation in addition with |
So I just picked up CoffeeScript---JavaScript, too, actually---so I might be a bit premature in weighing in my two cents. I'm actualy here, though, because I was getting caught up on compound comprehensions and trying to glean a complete understanding of CoffeeScript's implicit returns. It's not that I found them tricky or am taking fault in them, it's just that I must have missed the memo when I was @jashkenas, you said a couple times that one of the habits CoffeeScript is designed to promote, if not force, in its user-base is paying close attention to the return values of functions. It strikes me, though, that the syntactic sugar of implicit returns (as opposed to a syntax error in the absence of one) does just the opposite. And sure, the major headdesk experiences that were related in (or, you know... This whole issue?) will definitely hammer home the point that this language isn't as light'n fluffy as it looks, and you need to always be aware of what it is you're doing. There are definitely potential problems with this kind of syntax, especially in cases where programmers slip or get lazy. I would posit that those issues are the exact same issues that come along with potentially forgetting about an implicit return, though. Even with the inversion of control flow that specifying Well... That was a novel I didn't expect to write tonight. Keep up the good work. |
Sorry to beat the dead horse on this, 1. Classes and constructor functions
These two both work as intended with the class keyword, even though it goes against most coffeescript logic. The Ex1 constructor does exactly the same thing as the Ex2 constructor, Ex2 will not produce a return in the js as it is a class constructor - i.e. Now compare if you were to construct these classes prototypically
Ex1 will again work because the body of the constructor is inlined.
Ex2 will return @A as it is not understood to be a class constructor. It is clear that you already special case class constructors It is also very common to try to modify a class starting out looking 2. Implicit returns are only implicit at the endA simple case:
This works as intended, but you notice that the else statement is unnecessary
This only returns if b. Adding statements
This only returns c, and also end up discouraging the partial breakout return style First, the unreadible, inconsistent return breakout-style:
Second, the verbose if-else chain that only works at the end:
Third, the non-breaking out
The third also commonly seen with some returns littered randomly Obviously, while this is taken to the extreme, these are all pretty stupid. 3. It encourages being too cleverFirstly, the bad styles above are encouraged to avoid writing return, Secondly, we also try to get away with not adding an extra line
This makes it very hard to reason with the code you are not familiar with, 4. A huge part of js functions are side-effects onlySide-effects functions all suffer from problems already discussed in this issue, This is ultimately a very big annoyance, because it seriously detracts from the
The endThis issue isnt just about whether or return or null is too hard to write, but that I understand that my complaints in point 2 can be solved entirely by being Therefore, I think that having a symbol to indicate that this is a side-effect As the Ok, I am done. Sorry about the wall of text. |
I'm not convinced at all. Still a very strong -1. |
@michaelficarra I completely disagree with your argument against 4 (just using the return value). I am not worried about the overhead,
Besides, changing one character at the very beginning of function declarations is hardly the same as as adding an entire line, As you say: just use the return values. This is what everyone wants to do, because there is less code involved. |
I hate that argument. If the return value of a function is being observed and acted upon, it is, by definition, NOT a callback.Callbacks are in the tail position. They replace the currently executing function (well, technically they can't do that until es-next, but that's a spec. bug). When you have fears that passing a callback function to an event listener may produce a "bad" value, I just stop listening. Decide whether you want to care about your return value or not, don't play both sides.
Constructors are already capitalised as convention. Just like the developer thinks about capitalisation and thinks about the fact that they will be using the
This isn't Perl. Our syntax isn't a symbol soup, but it's not overly verbose either.
Your syntax does not protect you from early exits. You still have to analyse the function. Only implicit returns are protected, and those can be just as easily observed by looking at the single last expression of the function.
As I said above, you will always have to be thinking about your function's return value (assuming it's being used), regardless of whether it's specified implicitly or explicitly, at the head or at the tail. Even more so when the shitty libraries everyone seems to use are making sure a callback's return value isn't on their no-no list... |
Unfortunately, the definition of a callback matters little to how people write code. People are passing functions around and doing things with them. I know you have many legit arguments here, and I am sorry if this annoys you, but you can't defend own behaviour by calling other libraries shitty. Most code has flaws, and your code will often have to interface with flawed code. When that happens, I would rather my code made it absolutely unambiguous whose fault it was when something goes wrong. If I leak data through a passed in function, I am partially to blame if whatever weird API I am using optionally acts on that.
That's why the suggestion is up to make non-blank returns from such functions a syntax error. As for the prettiness/ugliness of said syntax, I can't argue with that. It's ultimately not my language. |
For what it's worth, I just spent a long time figuring out an issue related to this. In the jake build tool, asynchronous tasks must end with a call to "complete()" which emits the necessary event to trigger the continued execution of the build process. I was porting a JS Jakefile and I allowed these functions to end with complete, which did the wrong thing. Returning the function caused the build script to terminate. So the script would complete the one build step and never progress. I'm new to CS, just learning it. And I have now learned this lesson. At the very least, more could be made about the subtle mistakes this can cause in the introductory materials. I'm pretty sure it would have rang a bell and saved me a bunch of debugging. I'm not saying the language should change. I'm liking it very much. It would be useful to beginners to have the potential pitfalls of accidentally returning value called out clearly in the "Everything is an Expression" section of the overview. Maybe just an example of how to prevent it (other web pages are suggesting people return "true", so they clearly didn't get the message) would be sufficient. It's obvious it should be a naked "return" as soon as you're shown it once, but not until. |
Would it be safe to assume that really only loops as last statement are a concern here, since they have to be collected? I don't see any other case of implicit returns that would cause a problem here... |
@enyo: You're correct, which is exactly why we should be required to explicitly differentiate between loops that collect values and loops that just iterate. One of the two big changes I would make to CS if I could. |
Loops is definitely one case, but I thought some other use cases have been covered in this thread, like callbacks where the return value matters (e.g. jQuery @michaelficarra, what's the other big change you would make? =) |
@michaelficarra That's what I thought.. so the problem is not actually if functions implicitly return the last statements. The discussion should be focused on loops. |
@aseemk Mh, yes but on that matter I would disagree on a syntax change. If a value is expected to return something, than the last statement should reflect it. |
@aseemk: Sure, I was kind of ignoring that reason. The other change I would make is to the class syntax, especially the context within a class body (it should be the prototype rather than the constructor). See all the executable class body issues for the various opinions about that. |
Simple. |
I've used CoffeeScript for about two years now, and this one gotcha still strikes me now and then. Usually this is harmless. However, there are a few cases where it can really screw things up. Case 1: Cancelling event handlers. If an event handler returns false, it is the same as calling event.stopPropagation(); event.preventDefault(). If you write an event handler that sometimes returns false, you're in for some very difficult-to-find bugs. Case 2: Wasteful memory allocation. If the last statement in your function is a loop, CoffeeScript will generate a list for you. While it's building this list, it could be wasting a lot of valuable memory. If you're running on Mobile Safari, you have very limited memory, and using too much will cause Mobile Safari to crash. This is a serious problem. I have met several rather vocal CoffeeScript opponents who consider this accidental creation of huge lists to be the #1 reason they do not use CoffeeScript. Memory allocation in JavaScript is very slow, so if you care about performance, you don't want this to ever happen. Also, this use case produces some needlessly obtuse JavaScript. Normally, in JavaScript, if you don't specify a return value, functions return undefined. In a lot of cases, unless I'm writing a simple one-liner, this is what I want. To get this behavior in CoffeeScript, I currently have to type undefined as the last line of every function that shouldn't return a value. This is pretty ugly, and seems against the spirit of CoffeeScript. To maintain backward compatibility, I propose that we add a simple new syntax to get functions that require explicit return values. They could look like --> instead of ->, or ==> instead of =>. Or something similar. Anything shorter than typing undefined all over the place would be lovely. |
Excuse me, but this problem is not isolated to "shitty libraries". All web browsers will cancel your event if you return "false". Essentially, I just want to be able to write functions like in regular JavaScript, where they return undefined by default unless you explicitly return something else. Accidentally returning stuff is wasteful and degrades performance. We got this implicit return value thing from Ruby, and you know Ruby isn't known for its performance. |
Much of the time, a function exists to perform some sort of transformation on its argument(s) and return a value. Being able to do this is very nice: square = (n) -> n * n Some languages try to have their cake and eat it, too: def square(n):
return n * n square = lambda n: n * n My view is that one construct is better than two, in this case. One is free to use |
This is a fork from issue 896. To review: It's common to write a function that performs a loop at the end but where the list generated by that loop should not be returned. The best current solution is to add
return
to the end of the function, but this feels a bit kludgy, especially in one-liners. For instance:Failing to do this is a common pitfall not just for CoffeeScript newcomers, but even for pros. Look at underscore.coffee, where
_.mixin
and_.times
both generate lists, making them far less efficient than their underscore.js brethren. I think this speaks to how unintuitive the trailingreturn
is.So, I propose that putting
void
in front of->
or=>
should tell the compiler: "This function is not supposed to return a value. Don't generate returns implicitly. And raise an error if return anything explicitly." Then the example above would becomeAnd underscore.coffee wouldn't have to grow 2 lines longer in order to fix
_.mixin
and_.times
:The
void ->
syntax is not only more succinct, but also more self-documenting than thereturn
syntax. If I'm relatively new to CoffeeScript and I see a trailing return, I'm baffled—what is that doing there? Whereas if I seevoid
, a keyword that isn't allowed in any other context in CoffeeScript, I can look it up immediately in the documentation.Thoughts?
[On further reflection: Instead of
void
, how about*->
? For instance, compareto the current
A pretty significant readability improvement, don't you think?]
The text was updated successfully, but these errors were encountered: