-
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
Remove implicit returns? #2477
Comments
I'm not sure I fully agree but last week I actually ran into a situation where I had to add an explicit return. I'm writing a tool in node that uses Dust templating and allows a person to utilize their own scripts in order to add helpers to the context before compiling. The source for this tool is written in CoffeeScript but up to this point the helper scripts were being authored in JavaScript. I recently made it possible for them to be authored with either and when writing tests/examples it doesn't help to show the benefit of CoffeeScript when I had to add all these seemingly meaningless returns in to the helper functions due to the fact that when you return from a helper function in Dust it causes output to be written to the stream. |
I largely agree, but there is at least one situation where I'd hate to see it go, which is one-liner functions. I most other cases, though, I find that they end up simply being a way for me to make a simple error that has no immediate visible effect on the program, but has a cumulative impact on performance. Even worse, if you're making a library and you accidentally return something in a function that should return nothing, your users might start using that return value, leaving you with the decision to make it an official part of the API or break existing code. As far as updating old code to use explicit returns, it would be very easy to make a tool to do that automatically. I'd suggest that if implicit returns are to go, we should have a nicer way of writing countRodents = (animals) ->
count = 0
for critter in animals
count++ if critter.order is 'rodentia'
count would become countRodents = (animals) ->
count = 0
for critter in animals
count++ if critter.order is 'rodentia'
<- count |
Yeah, not sure changing return syntax at the same time would be a good idea. square = (x) -> <- x * x vs. square = (x) -> return x * x |
To be clear, I do not think that the |
Just as a side-note, coco brings "!" to suppress implicit returns foo = !-> alert 'hi'
baz = !(x) -> alert "it's #{x.toUpperCase()}"
#or even
!function batBar
doNothing a, b, c |
Absolutely not. This would go against most every design decision of CoffeeScript. Functions produce a useful value. When you define a function just to modularise a bunch of side effects, that's a procedure, and that should certainly be the extraordinary case. Almost every function you define in a CoffeeScript program should care about its return value. This is also true about functions in the jQuery case you provide, except that the value they return is unconditionally I can somewhat agree on the point about the value of a loop. One of CoffeeScript's greatest failings is that it does not distinguish between a loop and a list comprehension. This is a discussion for a different thread, but I'll at least state my stance on it here: comprehensions should have to be explicitly syntactically differentiated from looping constructs, whose value should be |
|
I disagree. Not all functions produce a useful value, and in fact most functions are actually procedures as you say and do not return anything. This is why in many if not most popular languages returning a value is opt in rather than required. I don't see how this goes against every design decision of CoffeeScript. I see that CoffeeScript is trying to make things easier, but unfortunately in most cases it just makes code harder to read and more error prone than languages where returns are explicit. You end up working around the language when it doesn't do what you want, which in this case is most of the time. It just doesn't result in clear code. A better example of the third point above is calling another function from This doesn't prevent you from returning things from functions, it just makes it more explicit, more readable and less error prone. Can you give me examples where implicit returns are more clear than explicit ones? On Aug 4, 2012, at 1:46 PM, Michael Ficarrareply@reply.github.com wrote:
|
in C# |
That observation is subjective. It depends on your programming style and the application's requirements. When using a functional programming style, you never ever define procedures. When using an async style, passing around functions as continuations, it's a little more difficult to reason about them in the same way. I would consider callbacks neither functions nor procedures.
CoffeeScript promotes a functional style with short lambda syntax, easy function application, and (almost-)everything-is-an-expression while still being friendly to the procedural developers. Most functions in a CoffeScript program would require an explicit return on the last statement if we made this change. I don't think that would benefit our users because we encourage that functional style.
I wouldn't say it tries to make things "easier" in this case. I would say that it defaults to the most common usage. That might be easier for certain people in certain situations.
It's not an "error" that you forgot to consider what the return value of your function should be. You just assumed it would default to the same thing it does in JS, which is a fallacy. This is not JS. What you're asking for is this: when execution reaches the end of a function without hitting an explicit return, the function has implied that it will return
It's not about clarity, as the implicit return value you propose is no more "clear" than the current semantics' by any applicable definition of the word "clear". Maybe it's more intuitive to you, but not to me or anyone who comes from a functional programming background. |
See also #899 and satyr/coco#91 Note that coco has syntax to suppress implicit return.
This is simply not true. Especially properly written asynchronous code has no return values, since the callback's arguments are the return values. |
Of course it's more clear. You can see immediately when looking at a function with a Looking through the CoffeeScript source itself, there are hardly any functions that actually make use of the implicit return values. Many of the functions don't write the word Returning This isn't JS exactly but it is in a way in that it interoperates with JS libraries in both directions - JS with CS source and CS with JS source. So it must play nice with JS and matching the semantics here is important I think. CoffeeScript promotes functional programming, and it still can. It is likely that many CoffeeScript programs are already using sort of explicit returns, by putting the return value on the last line without explicitly writing the word |
I couldn't let this wild claim go unchecked, so I did some research. I picked a file at random from coffee-script's source itself: command.coffee. I counted:
What gets me is that you say
This is outrageous. The fact that coffee-script has implicit return causes programmers to not care about the return value, as I have just demonstrated above. This argument goes against what you stand for. Conclusion: Implicit return should not be the default. Defining a function with a non-implicit return should require less than or equal to the same number of characters that defining a function with implicit return requires. |
The CS compiler is nowhere near a good example of a well-written or a functional program (or an async-heavy program that would support your point better).
No, not at all. Functions (by definition) should always care about their return values -- the difference we're debating is in how it is specified. The current syntax allows you to more easily specify that the return value is the completion value of the final statement (because it is the implied value). @devongovett wants it to be easy to specify that the function produces
You're both making an error in terminology and not reading my comments. Either way, the value is being implicitly specified. We are just debating what value should be implied. Quoting myself:
|
Yes, glad you have understood what I am asking for. You still haven't answered why implicitly returning the result of an assignment, loop, or other case not involving an explicit |
I strongly believe that our users are defining functions with meaningful return values based on the calculations performed in their body, often generating the return value in the final statement of the body. I don't believe they often want to define functions that will produce an |
Speak for yourself. As this thread has shown, many users agree that this is a source of bugs and unexpected behavior. It sounds like you are excluding us as users of CoffeeScript. Please back up your claims and define "we" and "idiomatic coffeescript". People use CoffeeScript in many different ways, not just the way you personally use it. I've asked specific questions about why you think the current behavior of returning the results of assignments, loops and things from inside if statements is a good idea and given examples of the bugs that arise from this behavior. I'd like to see an example where this is truly useful. In those few cases where this is really what you want, it is easy to add a I'd really like to hear more opinions on this. |
I think there is a slight fallacy in the C# example above: C#'s lambdas are pretty much the only thing that has an implicit return, and multiline statements require an block with an explicit return. I don't have enough experience in functional languages to say whether or not implicit returns are the exception or the norm (aren't real functional languages always only single line statements, curried together?), but unless there is a clear distinction between single-line and multiline statements, implicit returns seem like a smell to me. The only thing that should ever have implicit returns are single line statements IMHO, and there should be a clear distinction between single and multiline statements? Or, something that Delphi did, a distinction between a function and a procedure. |
In practice you do. [ |
I don't agree with this proposal. I like that the general consensus is that it depends on what the general style of programming you're using though. If you are going for a functional style, implicit defined returns (as opposed to implicit undefined returns) are a cool thing, while if you're going for a Now, CoffeeScript is trying to encourage a functional style, so it makes writing code in that style easier. I could write Haskell code in a procedural style, but i would be fighting the language every time; it would be easier to use a language that makes procedural code easier, or change my coding style to match what the language proposes. The same can go the other way if try to code in a functional style in Java. There are other languages that also return the value of the last expression from functions; apart from functional languages, Ruby (which is very imperative) and Lisp are the first ones that come to my mind. In my experience, this convention made me think of empty
I think this is a good example of why implicit defined returns are a good idea.
I wonder the same. If i want the function to return a value, then there will always be an foo = ->
if someCondition
'some value'
else
'some other value' Having to explicitly say that i want the value of that On the other hand, if i want the value of the function to always be undefined, then writing the explicit empty foo = ->
if someCondition
doSomething()
return It's usually not necessary to write that empty Also, when a function is only important because its side effects, why would someone want to access its return value? :S Finally, i think Coco's syntax for |
I think you're generalizing your own flavor of functional programming as if it applies to every functional programming style. There are many different functional programming styles. If you want to refer to them in general I suggest you refer to a resource where this generalization is defined. The fact is that not all functions return a value, even in functional languages. That's why there's In some styles a function is defined as something that does a simple thing and does it well, nothing to do with whether it actually returns something. It could do what it does to an object, or to an array. Either way, your arguments are on thin ice if you base them on something supposedly definitive, namely functional programming style. |
You're right, sorry for that. You can clarify my previous statements a little by replacing all mentions of functional programming with "functional programming in a pure functional language". |
In that case, let me correct my statement: your arguments have their feet halfway in the water. I'm pretty sure you know CoffeeScript isn't a pure functional language. Indeed, it's very object-oriented. So why you'd want to force principles/paradigms from pure functional programming style to it is beyond me. |
CoffeeScript has features that help users using many paradigms. The class syntax is for people that want a classical OO style. The |
Sure, but not at the expense of others. The short lambda syntax is used by I'd bet everyone using CoffeeScript. So where implicit returns might please people favoring pure functional style (my style is very much functional, but I for one don't like implicit returns), does it offer anything useful or harmful for other people? 74.3 of all statistics are made up, but my guess is that pure functional style doesn't represent the majority of people using CoffeeScript. It's OK to have language features that a few use because they like them, but it's not OK if that choice is forced upon a majority that would rather not have it. Regardless, a lot more data is needed to make a big decision like this, so more opinions (and rationales behind the opinions) would be helpful. |
I have only recently started to try out CoffeeScript, and I like a lot of it. The implicit returns is one thing that has really started to drive me crazy though so I have to say I agree with this ticket. I think one of CoffeeScript's goals is to make it easier to write javascript without really interfering with your code. For most things this is true. For example writing When I write a function in javascript I expect it to have no return value unless I explicitly type It seems to me that this feature was added to prevent you from having to type In the end I think removing implicit returns will cause the language to behave more as expected, improve code readability, and cut down on annoying bugs. I understand it's not an easy change to make cause it would probably break backwards compatibility with a number of projects, but I would vote for it. |
For what it is worth, I really like implicit returns. I would like coffeescript less if they were removed. |
I don't know. I think that's a very bold claim. At most, i'd agree that it would behave more as expected by programmers who are used to languages that require explicit returns. For programmers that are used to the idea that a function is like a mapping from an input to an output, the idea of "returning" at some point doesn't seem so necessary. Consider the simple function: double = (n) ->
n * 2 I personally interpret that function as "the double of a number n is that number multiplied by 2". No need to explain it in terms of returning something at some point (i.e. as an imperative computation). That's why i think it reads better without a Yeah, yeah, i know: "but that's an unrealistically simple example!". But simple functions are very useful, and a lot of nice properties come from defining functions that are as simple as possible. We should always strive for simple functions. When doing higher order programming, being able to define a simple function in a simple and succinct way is awesomely useful. So i really like that CoffeeScript lets me write Now, all this comes from the perspective that functions are like a mapping from an input to an output; something like in maths. And i do believe that, for the most part, subroutines (just to use a more computer science friendly word) should be functions. But there are subroutines that are not functional in nature. Lots of languages don't distinguish these two concepts; they invent a I think that rather than sacrificing the convenience of having a nice function notation for the special case of procedures, we could adopt a special syntax for those cases. Maybe a "shouted" arrow |
I don't think anyone wants to remove implicit returns for single-expression functions. It's the multi-statement functions that it's problematic with. |
I think that would add more confusion than what it'd remove. If i have a simple function like: endGameMessage = (score) -> if score > 100 then "You're awesome!" else "Keep trying dude..." If i change it to a multi-line if for whatever reason (maybe i want to stick to the 80-characters-per-line rule, or i want to add more |
The current syntax to simply return is to simply write @Zyphrax - A list of people who're pissed about something isn't worth a lot. We could post lists of Why I Hate CoffeeScript blog posts too. |
@carlsmith jashkenas wrote in his post "It's only a problem with (a small number of) people who haven't really tried to use CoffeeScript seriously". The links were to illustrate that it is hardly a small number of people. Hence the quotes. I don't understand why you would want to post lists of Why I Hate CoffeeScript blog posts in a discussion looking to improve the language or our understanding of it. |
I love CoffeeScript, but your reaction to this very reasonable request from numerous people who also love the language comes across as combative and obstinate. If you can't take honest, constructive feedback, maybe it's time to try something else... Sent from my iPad
|
@jbarker4682 - Combative and obstinate?? This is just regular give and take in open source. None of us know we're right; we're just chucking ideas around. @Zyphrax - I think you're being a tad pedantic in your interpretation of that comment, but I never made the remark - I just found it agreeable. Re. lists of blog posts: There are people who think CoffeeScript fails for all sorts of reasons. The same's true of every programming language ever, and text editor and everything else we use. Does a list of people hating on the GIL mean Python's doomed? Just haters. It's also not really a "very reasonable request from numerous people". It's a pile of general criticism. Which patch have you all agreed needs merging? It's all hypothetical anyway, so let's at least keep the exchange academic. |
@carlsmith I feel that you see this discussion as an attack on the CoffeeScript language. I don't hate CoffeeScript, otherwise I wouldn't take part in an open discussion like this to further improve the language. As I wrote earlier I like CoffeeScript but the implicit returns feature is giving me quite some frustration with Angular and jQuery. So my first step was to read about it and to see what other people think about it. During that search I noticed that quite a lot of people run into this issue. So a few days ago I thought I'd chime in. And continue the discussion to find a solution. The response so far is: this isn't an issue and nothing will change. Submitting a patch is usually step 2 after you've completed step 1: discuss how/when/if to solve it. It seems this will die with step 1, which is really unfortunate. |
I realize this conversation is pretty old but I didn't get to enjoy reading all these thoughts until just this morning. So that we can all benefit from your knowledge, can somebody quickly describe the problem with returning the result of a comprehension? Is it just that it takes a bit of extra memory space to store the resulting array, which is unnecessary if we know that it will be discarded immediately? |
Yes, or a loop in general. |
In that case I can see why nobody's commented on this thread in years - why would you do a comprehension in the first place if you didn't intend to use the result. |
I mean "loop" by "comprehension". |
Ah.. I see now - thank you! |
@michaelficarra @jashkenas can I have a link to a project that makes use of that style of programming? It sounds interesting. CoffeeScript isn't a mere expression-based language. It's also far more convenient and easier to write than Javascript, which is why it's frequently used as a replacement for Javascript. Given that many of its users are indeed looking for CoffeeScript's convenience in a "side-effect-ful" environment, then there should be an option to disable implicit returns. I'm not sure on the details but CS must have some sort of compiler options, right? Disabling implicit returns shouldn't be hard to maintain. And any effort spent on it should be much smaller than the effort spent arguing about it and addressing the same issue over and over. I'm waiting for the links on the projects. |
@odraencoded: I don't think it's logically sound to reason that a thing should be done because discussing it costs more than implementing it. |
@odraencoded what programming style? |
I don't think this is a matter of programming style. coffeescript isn't a language on its own, it's a facade for javascript and this feature causes it to generate inefficient and sometimes nonsensical code which doesn't seem obvious by looking at the source; THAT'S why it doesn't create problems on languages that support it natively but it does here. so how about a pragma? something like |
Reading this entire thread, a few things become obvious. First, there is no way that the default behavior of Coffeescript is going to be changed. Ever. Second, the maintainers, knowing this, have worked backwards from that and convinced themselves that there isn't even a problem. Coffeescript is used for client side code. Event handlers are always procedural, in HTML. Whatever the functional goals of Coffeescript, HTML is the reality of where it lives. I agree that it shouldn't be changed but you are fooling yourself if think it was a good design decision in retrospect. I have written a reactive language with syntax inspired by Coffeescript and this is what I did: Single line lambda expressions have an implicit return.
Multiline functions require an explicit return. This works very well in practice. |
This also solves the related oddity of constructors behaving differently than other functions by not having an implicit return. |
This looks almost exactly like the C# one But at this point, removing it would probably break more code and cause more bugs than implicit returns in those situations ever did. |
@AustinBryan |
@krisnye - Using the arrow operator exclusively for single-expression lambdas with implicit returns (and a different syntax for functions with blocks and explicit returns) is a nice compromise. I've argued for the same thing on here in the past, but to offer a counter... CoffeeScript (unlike Python et al) allows blocks (with significant indentation) to be nested inside expressions. The blocks are expressions themselves. Apart from control flow statements, everything is an expression. If the language generally aims to make all blocks implicitly evaluate to value of the last subexpression, you would definitely expect that to be true for functions. Does your language evaluate branches and loops still, just not functions? Are any blocks expressions? |
@carlsmith My language did not evaluate all blocks as expressions. Instead, it allowed control flow statements (if/else/for) inside of nested object literals.
This proved extremely useful for declaring dependent structures like HTML components. Looked sort of like this.
|
Ok, cool. That's quite different to CoffeeScript syntax.
I like restricting the arrow operator to Pythonic lambdas, generally, but for CoffeeScript (where blocks are expressions, and can be significantly indented as subexpressions), there's a consistency-based argument for the current approach. |
After writing CoffeeScript for quite a long a while, I've come to the realization that implicit returns are actually more annoying than useful. Oftentimes I'll have to go back to a function later and add an explicit
return
statement to prevent CoffeeScript from adding implicit returns. They are the source of many bugs, and reduce the readability and predictability of CoffeeScript code.return
). Generating and returning that array when I very rarely want it can also have a potentially large performance impact on the function as well, so I always have to remember to add areturn
statement at the end of many of my functions to prevent this. Just looking at a function with a loop at the end doesn't tell me that it returns an array so when it turns out it does it can be kinda strange. Usually you end up finding out when you discover another bug related to it or you look at the compiled JavaScript.This function returns the result of
doSomething()
only ifsomeCondition
is truth-y, otherwise it returnsundefined
. The question is why I would ever want a return value sometimes and not others. Without an explicit return statement, this seems strange. It isn't clear that this function returns anything just from looking at it.each
function, which uses thefalse
sentinel return value as a way to break out of the loop. Using the function in CoffeeScript when the last operation in that function sets a variable tofalse
can be a source of bugs since CoffeeScript implicitly returnsfalse
and this stops the loop. For example:Just reading this example doesn't make it clear that the loop will only run once rather than the expected three times. The variable
map
would equal{ a: false }
after running this loop. I would have expected to have gotten{ a: false, b: false, c: false }
. Adding an explicitreturn
as the last line of the each function fixes the problem, but you wouldn't know to do that without reading the compiled JavaScript.I could probably come up with many more examples to show that implicit returns are usually not a good idea. I think CoffeeScript should actually remove implicit returns from the language. Typing out
return
really isn't so hard and will lead to more readable and predictable code and keep the language closer to the semantics of JavaScript (a good thing when working with libraries written in JavaScript like jQuery from CoffeeScript code). It would probably be less typing in the end since you will no longer have to add return statements to prevent CoffeeScript from implicitly returning the wrong thing. I know this would be a large change but I think it would be for the better since it is more of a cause of bugs than a helpful language feature.Removing features from a language is hard, but sometimes the best thing to do. What do you think?
The text was updated successfully, but these errors were encountered: