jashkenas / coffeescript

Unfancy JavaScript
https://coffeescript.org/
MIT License
16.49k stars 1.98k forks source link

Remove implicit returns? #2477

Closed devongovett closed 12 years ago

devongovett commented 12 years ago

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.

  1. Having to go back an add explicit returns happens especially often when a loop is the last thing in the function, where very rarely do I want the function to return an array (if I did, I could easily add an explicit 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 a return 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.
  2. It can also be inconsistent when combined with conditional logic. For example:
test = ->
        if someCondition
            doSomething()

This function returns the result of doSomething() only if someCondition is truth-y, otherwise it returns undefined. 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.

  1. It can be annoying for functions that do something based on another function's return value. An example of such a function is jQuery's each function, which uses the false 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 to false can be a source of bugs since CoffeeScript implicitly returns false and this stops the loop. For example:
arr = ['a', 'b', 'c']
map = {}
$.each arr, ->
        map[this] = false

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 explicit return 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?

jashkenas commented 9 years ago

It's not a problem with CoffeeScript. It's only a problem with (a small number of) people who haven't really tried to use CoffeeScript seriously. If you write it in earnest with the expectation that every function returns a meaningful value, then you're not going to be surprised by return values, or inconvenienced.

Side-effect-ful or value-ful programming is a style you use, not something inherent to programming in JS in any particular environment. CoffeeScript in a value-ful style is lovely, CoffeeScript in a side-effect-ful style is just fine — you'll merely be writing more return than you would otherwise. No big deal.

devongovett commented 9 years ago

Let's not make assumptions. I opened this issue over two years ago and use CoffeeScript very seriously.

Unfortunately, this is just something you have to get used to if you use CoffeeScript. For most functions, returning a value where one isn't needed is fine. For performance critical code (like some that I linked to above), it is necessary to add extra returns if the function ends with a loop or in weird edge cases like others have mentioned.

crissdev commented 9 years ago

@devongovett Great work :+1:

yvbeek commented 9 years ago

"It's not a problem with CoffeeScript. It's only a problem with (a small number of) people who haven't really tried to use CoffeeScript seriously".

It is of course your own choice to do something about this issue or not, but remarks like this will only push people away from CoffeeScript. It is similar to Apple's you-are-holding-it-wrong attitude. There are plenty of situations in Javascript where you don't want to return a value from your function.

Here are a "small" number of people having issues with implicit returns: StackOverflow, StackOverflow 2, StackOverflow 3, StackOverflow 4 Why I hate implicit return in CoffeeScript jQuery and CoffeeScript: trouble with automatic return While I love CoffeeScript, the always-implicit-return thing is my biggest pet peeve

Just voicing my opinion here :)

demisx commented 9 years ago

@Zyphrax :+1: Well said. We use CS very seriously and there are plenty of places in code where a function doesn't need take return any values. Take Mocha or Protractor tests for example.

P.S. I personally don't consider implicit returns being a problem in CS, but I also think the discussion should be open and the attitude needs to be more friendly and welcoming. We don't want to turn away people from CS and have them miss out on all of the benefits it brings along.

hoosierhobbyist commented 9 years ago

Hello all, my name is Seth, and I just discovered CS about a week ago. Personally, I love it especially when compared to JS. Right now I'm using it to write a simple gaming engine just for fun and practice. I've been following this conversation all day and I would just like to put my two cents in. I did find the implicit returns a little confusing at first, but now that I understand them, I cant imagine this language without them. That being said, there are many situations I've come across where I have to explicitly return nothing, and it is a minor annoyance. I'm certainly not the person to do it, but I do believe a new syntax to just simply 'return' would be a great boon to a lot of people.

— Sent from Mailbox

On Mon, Nov 3, 2014 at 6:22 PM, Dmitri Moore notifications@github.com wrote:

@Zyphrax :+1: Well said. We use CS very seriously and there are plenty of places in code where a function doesn't need take return any values. Take Mocha or Protractor tests for example.

P.S. I personally don't consider implicit returns being a problem in CS, but I also think the discussion should be open and the attitude needs to be more friendly and welcoming. We don't want to turn away people from CS and have them miss out on all of the benefits it brings along.

Reply to this email directly or view it on GitHub: https://github.com/jashkenas/coffeescript/issues/2477#issuecomment-61568086

carlsmith commented 9 years ago

The current syntax to simply return is to simply write return at any point in the function you wish to simply return from. Implicit returns is a case of not being able to have your cake and eat it.

@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.

yvbeek commented 9 years ago

@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.

ghost commented 9 years ago

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

On Nov 4, 2014, at 9:21 AM, Zyphrax notifications@github.com wrote:

@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.

— Reply to this email directly or view it on GitHub.

carlsmith commented 9 years ago

@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.

yvbeek commented 9 years ago

@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.

macgyver commented 9 years ago

the exception to the rule is when your function ends with a loop, and you're returning a comprehension unnecessarily

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?

vendethiel commented 9 years ago

Yes, or a loop in general.

macgyver commented 9 years ago

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.

vendethiel commented 9 years ago

I mean "loop" by "comprehension".

macgyver commented 9 years ago

Ah.. I see now - thank you!

odraencoded commented 9 years ago

@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.

macgyver commented 9 years ago

@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.

michaelficarra commented 9 years ago

@odraencoded what programming style?

git2samus commented 8 years ago

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 'use explicit' to mimic the 'use strict' which people is familiar with, that would be opt-in thus keeping compatibity and could help with performance critic scenarios and also those who break with third-party libs.

krisnye commented 8 years ago

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.

double = (x) -> x * 2

Multiline functions require an explicit return.

This works very well in practice.

krisnye commented 8 years ago

This also solves the related oddity of constructors behaving differently than other functions by not having an implicit return.

AustinBryan commented 6 years ago

This looks almost exactly like the C# one => and we can use it for one-line method bodies as well, and I've never had any of those problems, because it doesn't make those wild assumptions. So, if anything, it should just remove it from those instances.

But at this point, removing it would probably break more code and cause more bugs than implicit returns in those situations ever did.

Inve1951 commented 6 years ago

@AustinBryan => already has meaning in coffeescript. implicit returns have no performance implications as every function alway returns a value, undifined if you don't override it. the only times you have to put an empty return is when the function is required to return undefined or to prevent returning a loop.

carlsmith commented 5 years ago

@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?

krisnye commented 5 years ago

@carlsmith My language did not evaluate all blocks as expressions. Instead, it allowed control flow statements (if/else/for) inside of nested object literals.

let object = {}
    x: 10
    y: 20
    if foo
        z: 30

This proved extremely useful for declaring dependent structures like HTML components. Looked sort of like this.

Div()
    for name, task of tasks
        Div()
            task.name
            if task.complete
                Button()
                     "Remove"
carlsmith commented 5 years ago

Ok, cool. That's quite different to CoffeeScript syntax.

I agree that [CoffeeScript's grammar for the arrow operators] shouldn't be changed but you are fooling yourself if think it was a good design decision in retrospect.

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.