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

Closed
devongovett opened this Issue Aug 4, 2012 · 123 comments

Comments

Projects
None yet
@devongovett

devongovett commented Aug 4, 2012

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?

@matthewrobb

This comment has been minimized.

Show comment
Hide comment
@matthewrobb

matthewrobb Aug 4, 2012

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.

matthewrobb commented Aug 4, 2012

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.

@osuushi

This comment has been minimized.

Show comment
Hide comment
@osuushi

osuushi Aug 4, 2012

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 return, and I'd nominate <- which has a nice symmetry with the function marker. So

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

osuushi commented Aug 4, 2012

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 return, and I'd nominate <- which has a nice symmetry with the function marker. So

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

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 4, 2012

Yeah, not sure changing return syntax at the same time would be a good idea. return really isn't that long, and it is very clear what it does whereas something like <- is just one more thing to learn. Plus, one liners would look weird.

square = (x) -> <- x * x

vs.

square = (x) -> return x * x

devongovett commented Aug 4, 2012

Yeah, not sure changing return syntax at the same time would be a good idea. return really isn't that long, and it is very clear what it does whereas something like <- is just one more thing to learn. Plus, one liners would look weird.

square = (x) -> <- x * x

vs.

square = (x) -> return x * x
@osuushi

This comment has been minimized.

Show comment
Hide comment
@osuushi

osuushi Aug 4, 2012

To be clear, I do not think that the <- syntax would work well for one-liners. I think they would only be a good fit if one-liners still had implicit returns.

osuushi commented Aug 4, 2012

To be clear, I do not think that the <- syntax would work well for one-liners. I think they would only be a good fit if one-liners still had implicit returns.

@vendethiel

This comment has been minimized.

Show comment
Hide comment
@vendethiel

vendethiel Aug 4, 2012

Collaborator

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
Collaborator

vendethiel commented Aug 4, 2012

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

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Aug 4, 2012

Collaborator

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 undefined. What's so wrong with specifying that? CoffeeScript is just trying to make it easier since, in the vast majority of cases, when execution hits the last statement of a function, the author would like the function to produce that value.

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 null or some more appropriate value -- but not a list.

Collaborator

michaelficarra commented Aug 4, 2012

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 undefined. What's so wrong with specifying that? CoffeeScript is just trying to make it easier since, in the vast majority of cases, when execution hits the last statement of a function, the author would like the function to produce that value.

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 null or some more appropriate value -- but not a list.

@jussi-kalliokoski

This comment has been minimized.

Show comment
Hide comment
@jussi-kalliokoski

jussi-kalliokoski Aug 4, 2012

void ALL THE THINGS. ;)

jussi-kalliokoski commented Aug 4, 2012

void ALL THE THINGS. ;)

@devongovett

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 4, 2012

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
within the jQuery each function, where you don't necessarily know what it returns. It could return false and break your code if you aren't careful to explicitly return nothing. This causes a whole class of bugs and makes CoffeeScript harder for newcomers, especially for those coming from JavaScript. CoffeeScript is suppose to have the semantics of JavaScript with a better syntax, and this is one of the rare cases where that isn't the case IMO.

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:

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 undefined. What's so wrong with specifying that? CoffeeScript is just trying to make it easier since, in the vast majority of cases, when execution hits the last statement of a function, the author would like the function to produce that value.

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 null or some more appropriate value -- but not a list.


Reply to this email directly or view it on GitHub:
#2477 (comment)

devongovett commented Aug 4, 2012

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
within the jQuery each function, where you don't necessarily know what it returns. It could return false and break your code if you aren't careful to explicitly return nothing. This causes a whole class of bugs and makes CoffeeScript harder for newcomers, especially for those coming from JavaScript. CoffeeScript is suppose to have the semantics of JavaScript with a better syntax, and this is one of the rare cases where that isn't the case IMO.

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:

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 undefined. What's so wrong with specifying that? CoffeeScript is just trying to make it easier since, in the vast majority of cases, when execution hits the last statement of a function, the author would like the function to produce that value.

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 null or some more appropriate value -- but not a list.


Reply to this email directly or view it on GitHub:
#2477 (comment)

@ScottWeinstein

This comment has been minimized.

Show comment
Hide comment
@ScottWeinstein

ScottWeinstein Aug 5, 2012

in C# people.Select(x => x.Name)
in CS _.map(people, x -> x.Name)

ScottWeinstein commented Aug 5, 2012

in C# people.Select(x => x.Name)
in CS _.map(people, x -> x.Name)

@michaelficarra

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Aug 5, 2012

Collaborator

Not all functions produce a useful value, and in fact most functions are actually procedures

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.

I don't see how this goes against every design decision of CoffeeScript.

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 see that CoffeeScript is trying to make things easier

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.

This doesn't prevent you from returning things from functions, it just makes it more explicit, more readable and less error prone. [...] CoffeeScript is suppose to have the semantics of JavaScript with a better syntax

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 undefined at that point. CoffeeScript functions currently imply that they will return the completion value of the last statement. They're both implicit, but you claim that producing the useless undefined value is the more desirable behaviour. I would have to disagree on behalf of the majority of our users.

Can you give me examples where implicit returns are more clear than explicit ones?

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.

Collaborator

michaelficarra commented Aug 5, 2012

Not all functions produce a useful value, and in fact most functions are actually procedures

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.

I don't see how this goes against every design decision of CoffeeScript.

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 see that CoffeeScript is trying to make things easier

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.

This doesn't prevent you from returning things from functions, it just makes it more explicit, more readable and less error prone. [...] CoffeeScript is suppose to have the semantics of JavaScript with a better syntax

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 undefined at that point. CoffeeScript functions currently imply that they will return the completion value of the last statement. They're both implicit, but you claim that producing the useless undefined value is the more desirable behaviour. I would have to disagree on behalf of the majority of our users.

Can you give me examples where implicit returns are more clear than explicit ones?

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.

@andrewrk

This comment has been minimized.

Show comment
Hide comment
@andrewrk

andrewrk Aug 5, 2012

See also #899 and satyr/coco#91

Note that coco has syntax to suppress implicit return.

Most functions in a CoffeScript program would require an explicit return on the last statement if we made this change.

This is simply not true. Especially properly written asynchronous code has no return values, since the callback's arguments are the return values.

andrewrk commented Aug 5, 2012

See also #899 and satyr/coco#91

Note that coco has syntax to suppress implicit return.

Most functions in a CoffeScript program would require an explicit return on the last statement if we made this change.

This is simply not true. Especially properly written asynchronous code has no return values, since the callback's arguments are the return values.

@devongovett

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 5, 2012

Of course it's more clear. You can see immediately when looking at a function with a return statement that it will return a value and what value it will return. With an implicit style, you may have to hunt around. Why is it useful to have the return value of a function be the last thing it does? In the case of the jQuery example, you're assigning a variable to false. How is that useful as a return value? There is no distinction of when you actually want to return something from the function and when you did so "by accident" so to speak.

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 return but include the return value by itself at the end of the function, which is basically the same thing. Adding a return statement in front of that wouldn't be so bad, and would probably help highlight what is being returned. But why would you want the return value of a function to be the result of an assignment or another of the examples I mentioned above unless you explicitly told it to? I'm not seeing any place in the CS source or any other project that I've seen where that is actually useful.

Returning undefined by default is infinitely more useful. You don't "accidentally" expose internal functionality to the outside world by implicitly returning it when you forget to add a return at the end. You improve performance for functions with loops or comprehensions at the end where you might not want an array to be returned. undefined is sort of the unvalue value. It is a value that represents nothing, and so it is useful. For functions like the jQuery case that do something based on the return value, you only want to return something other than undefined when you explicitly want to do so.

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 return. The other functions that are not really supposed to return anything (or undefined as you would say) but don't because of a mistake of the programmer (assignments at the end of functions, loops, etc.) would simply be corrected to return undefined. Yeah, you'd have to write return at the end of your functions, but in my opinion that will make your functions more clear as to what they are returning, and since you likely already put your return values at the end of your functions without the word "return", it isn't really so hard.

devongovett commented Aug 5, 2012

Of course it's more clear. You can see immediately when looking at a function with a return statement that it will return a value and what value it will return. With an implicit style, you may have to hunt around. Why is it useful to have the return value of a function be the last thing it does? In the case of the jQuery example, you're assigning a variable to false. How is that useful as a return value? There is no distinction of when you actually want to return something from the function and when you did so "by accident" so to speak.

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 return but include the return value by itself at the end of the function, which is basically the same thing. Adding a return statement in front of that wouldn't be so bad, and would probably help highlight what is being returned. But why would you want the return value of a function to be the result of an assignment or another of the examples I mentioned above unless you explicitly told it to? I'm not seeing any place in the CS source or any other project that I've seen where that is actually useful.

Returning undefined by default is infinitely more useful. You don't "accidentally" expose internal functionality to the outside world by implicitly returning it when you forget to add a return at the end. You improve performance for functions with loops or comprehensions at the end where you might not want an array to be returned. undefined is sort of the unvalue value. It is a value that represents nothing, and so it is useful. For functions like the jQuery case that do something based on the return value, you only want to return something other than undefined when you explicitly want to do so.

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 return. The other functions that are not really supposed to return anything (or undefined as you would say) but don't because of a mistake of the programmer (assignments at the end of functions, loops, etc.) would simply be corrected to return undefined. Yeah, you'd have to write return at the end of your functions, but in my opinion that will make your functions more clear as to what they are returning, and since you likely already put your return values at the end of your functions without the word "return", it isn't really so hard.

@andrewrk

This comment has been minimized.

Show comment
Hide comment
@andrewrk

andrewrk Aug 5, 2012

Most functions in a CoffeScript program would require an explicit return on the last statement if we made this change.

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:

  • 8 instances of implicit return being helpful
  • 40 instances of implicit return being applied to a function which was not meant to return a value
  • 2 instances of unintentionally returning the results of a for loop
  • 1 instance of explicitly using a return statement at the end of a function body to avoid this

What gets me is that you say

Almost every function you define in a CoffeeScript program should care about its return value.

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.

andrewrk commented Aug 5, 2012

Most functions in a CoffeScript program would require an explicit return on the last statement if we made this change.

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:

  • 8 instances of implicit return being helpful
  • 40 instances of implicit return being applied to a function which was not meant to return a value
  • 2 instances of unintentionally returning the results of a for loop
  • 1 instance of explicitly using a return statement at the end of a function body to avoid this

What gets me is that you say

Almost every function you define in a CoffeeScript program should care about its return value.

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.

@michaelficarra

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Aug 5, 2012

Collaborator

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

The fact that coffee-script has implicit return causes programmers to not care about the return value, as I have just demonstrated above.

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 undefined. If people are not considering the return values of their functions, they are either intending them to be used as procedures or they are making an error. For the third case, using a function as a continuation, I agree that the current implicit return semantics are not useful, but I would argue that the solution is to specify a call/cc differently, not to change how functions are specified.

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.

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:

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 undefined at that point. CoffeeScript functions currently imply that they will return the completion value of the last statement. They're both implicit, but you claim that producing the useless undefined value is the more desirable behaviour.

Collaborator

michaelficarra commented Aug 5, 2012

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

The fact that coffee-script has implicit return causes programmers to not care about the return value, as I have just demonstrated above.

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 undefined. If people are not considering the return values of their functions, they are either intending them to be used as procedures or they are making an error. For the third case, using a function as a continuation, I agree that the current implicit return semantics are not useful, but I would argue that the solution is to specify a call/cc differently, not to change how functions are specified.

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.

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:

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 undefined at that point. CoffeeScript functions currently imply that they will return the completion value of the last statement. They're both implicit, but you claim that producing the useless undefined value is the more desirable behaviour.

@devongovett

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 5, 2012

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 return is more useful or more clear than implicitly returning undefined, as you have put it. I have explained why I think implicitly returning anything other than undefined is a bad idea. All I'm trying to do is save myself from writing return nothing lines at the end of many of my functions to work around a class of bugs related to the current behavior.

devongovett commented Aug 5, 2012

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 return is more useful or more clear than implicitly returning undefined, as you have put it. I have explained why I think implicitly returning anything other than undefined is a bad idea. All I'm trying to do is save myself from writing return nothing lines at the end of many of my functions to work around a class of bugs related to the current behavior.

@michaelficarra

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Aug 5, 2012

Collaborator

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 undefined value after performing some side effects. That's an uncommon practise in idiomatic coffeescript, and we'd like to make that slightly less convenient to encourage use of the functional paradigm.

Collaborator

michaelficarra commented Aug 5, 2012

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 undefined value after performing some side effects. That's an uncommon practise in idiomatic coffeescript, and we'd like to make that slightly less convenient to encourage use of the functional paradigm.

@devongovett

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 5, 2012

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 return to make things work as before, and adding it would make the code more clear anyway. Explicit returns are more clear because you know what the return value will be if you don't provide one: undefined. The current behavior is different for every function.

I'd really like to hear more opinions on this.

devongovett commented Aug 5, 2012

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 return to make things work as before, and adding it would make the code more clear anyway. Explicit returns are more clear because you know what the return value will be if you don't provide one: undefined. The current behavior is different for every function.

I'd really like to hear more opinions on this.

@mstum

This comment has been minimized.

Show comment
Hide comment
@mstum

mstum Aug 5, 2012

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.

mstum commented Aug 5, 2012

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.

@satyr

This comment has been minimized.

Show comment
Hide comment
@satyr

satyr Aug 5, 2012

Collaborator

When using a functional programming style, you never ever define procedures

In practice you do. [unit -> unit](takes/returns nothing) is quite common in OCaml for example.

Collaborator

satyr commented Aug 5, 2012

When using a functional programming style, you never ever define procedures

In practice you do. [unit -> unit](takes/returns nothing) is quite common in OCaml for example.

@epidemian

This comment has been minimized.

Show comment
Hide comment
@epidemian

epidemian Aug 5, 2012

Contributor

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 messy very stateful style, they may not be so cool.

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 returns as a code smell/warning. When i see one in CoffeeScript, i always think to myself "why is this function not returning anything? Can i do something to make this function more like a normal function and get rid of that ugly empty return?". It's like a goto or an empty catch =P

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.

I think this is a good example of why implicit defined returns are a good idea.

The question is why I would ever want a return value sometimes and not others.

I wonder the same. If i want the function to return a value, then there will always be an else case, because i want the return value to always be defined. I find myself writing code like this very often:

foo = ->
  if someCondition
    'some value'
  else
    'some other value'

Having to explicitly say that i want the value of that if expression to be the return value of the function is redundant.

On the other hand, if i want the value of the function to always be undefined, then writing the explicit empty return makes that intent... well... more explicit:

foo = ->
  if someCondition
    doSomething()
  return

It's usually not necessary to write that empty return though. When a function is only important because its side effects, then the last function it calls most surely also only important because its side effects, so it should also have an undefined return value; no need to write that empty return :)

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 undefined-returning functions (or "procedures", as @michaelficarra calls them =P) is a cool compromise. The ! is kind of a warning: "hey, this function does not return anything, so it surely has some kind of side-effect, be careful!". Scheme uses the same symbol to express mutability as a convention; Ruby does something similar too.

Contributor

epidemian commented Aug 5, 2012

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 messy very stateful style, they may not be so cool.

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 returns as a code smell/warning. When i see one in CoffeeScript, i always think to myself "why is this function not returning anything? Can i do something to make this function more like a normal function and get rid of that ugly empty return?". It's like a goto or an empty catch =P

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.

I think this is a good example of why implicit defined returns are a good idea.

The question is why I would ever want a return value sometimes and not others.

I wonder the same. If i want the function to return a value, then there will always be an else case, because i want the return value to always be defined. I find myself writing code like this very often:

foo = ->
  if someCondition
    'some value'
  else
    'some other value'

Having to explicitly say that i want the value of that if expression to be the return value of the function is redundant.

On the other hand, if i want the value of the function to always be undefined, then writing the explicit empty return makes that intent... well... more explicit:

foo = ->
  if someCondition
    doSomething()
  return

It's usually not necessary to write that empty return though. When a function is only important because its side effects, then the last function it calls most surely also only important because its side effects, so it should also have an undefined return value; no need to write that empty return :)

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 undefined-returning functions (or "procedures", as @michaelficarra calls them =P) is a cool compromise. The ! is kind of a warning: "hey, this function does not return anything, so it surely has some kind of side-effect, be careful!". Scheme uses the same symbol to express mutability as a convention; Ruby does something similar too.

@jussi-kalliokoski

This comment has been minimized.

Show comment
Hide comment
@jussi-kalliokoski

jussi-kalliokoski Aug 5, 2012

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.

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 void (not void *) in C. Some of the functional programming styles are also mixtures of object-oriented and functional style, as an example take a look at the czmq source. There the function may be more of a method that just does something to the object at hand and never returns anything. One could argue that those functions should return an error code, but not all functions have any points of failure. One could also argue that CoffeeScript is a functional language, but it's really more of a mixture, after all there are classes and objects.

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.

jussi-kalliokoski commented Aug 5, 2012

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.

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 void (not void *) in C. Some of the functional programming styles are also mixtures of object-oriented and functional style, as an example take a look at the czmq source. There the function may be more of a method that just does something to the object at hand and never returns anything. One could argue that those functions should return an error code, but not all functions have any points of failure. One could also argue that CoffeeScript is a functional language, but it's really more of a mixture, after all there are classes and objects.

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.

@michaelficarra

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Aug 5, 2012

Collaborator

I think you're generalizing your own flavor of functional programming as if it applies to every 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".

Collaborator

michaelficarra commented Aug 5, 2012

I think you're generalizing your own flavor of functional programming as if it applies to every 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".

@jussi-kalliokoski

This comment has been minimized.

Show comment
Hide comment
@jussi-kalliokoski

jussi-kalliokoski Aug 5, 2012

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.

jussi-kalliokoski commented Aug 5, 2012

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.

@michaelficarra

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Aug 5, 2012

Collaborator

CoffeeScript has features that help users using many paradigms. The class syntax is for people that want a classical OO style. The :: operator is for those that want OOP while accepting JS's prototypal inheritance. And the short lambda syntax is for functional programmers. So shouldn't the functional programmers' favoured semantics be used?

Collaborator

michaelficarra commented Aug 5, 2012

CoffeeScript has features that help users using many paradigms. The class syntax is for people that want a classical OO style. The :: operator is for those that want OOP while accepting JS's prototypal inheritance. And the short lambda syntax is for functional programmers. So shouldn't the functional programmers' favoured semantics be used?

@jussi-kalliokoski

This comment has been minimized.

Show comment
Hide comment
@jussi-kalliokoski

jussi-kalliokoski Aug 5, 2012

And the short lambda syntax is for functional programmers. So shouldn't the functional programmers' favoured semantics be used?

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.

jussi-kalliokoski commented Aug 5, 2012

And the short lambda syntax is for functional programmers. So shouldn't the functional programmers' favoured semantics be used?

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.

@ccampbell

This comment has been minimized.

Show comment
Hide comment
@ccampbell

ccampbell Aug 6, 2012

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 for thing in things is much simpler than writing for (var i = 0, length = things.length; i < length; i++), however when it comes to functions this is one area where it behaves the opposite of vanilla javascript.

When I write a function in javascript I expect it to have no return value unless I explicitly type return. Anyone reading the code will easily be able to see that in one function you are returning a value, but in another function you are not. CoffeeScript does the opposite which is not only unintuitive for people who are new to the language or reading the code, but can also lead to bugs as mentioned above.

It seems to me that this feature was added to prevent you from having to type return all the time, but I find myself having to type return even more often when using CoffeeScript than I do when I am writing regular javascript (to prevent implicit returns).

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.

ccampbell commented Aug 6, 2012

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 for thing in things is much simpler than writing for (var i = 0, length = things.length; i < length; i++), however when it comes to functions this is one area where it behaves the opposite of vanilla javascript.

When I write a function in javascript I expect it to have no return value unless I explicitly type return. Anyone reading the code will easily be able to see that in one function you are returning a value, but in another function you are not. CoffeeScript does the opposite which is not only unintuitive for people who are new to the language or reading the code, but can also lead to bugs as mentioned above.

It seems to me that this feature was added to prevent you from having to type return all the time, but I find myself having to type return even more often when using CoffeeScript than I do when I am writing regular javascript (to prevent implicit returns).

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.

@phaedryx

This comment has been minimized.

Show comment
Hide comment
@phaedryx

phaedryx Aug 6, 2012

For what it is worth, I really like implicit returns. I would like coffeescript less if they were removed.

phaedryx commented Aug 6, 2012

For what it is worth, I really like implicit returns. I would like coffeescript less if they were removed.

@epidemian

This comment has been minimized.

Show comment
Hide comment
@epidemian

epidemian Aug 6, 2012

Contributor

In the end I think removing implicit returns will cause the language to behave more as expected

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 return there.

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 areas = figures.map (f) -> f.area(). I think that forcing a return for all "valued" functions would make these kind of nested functions more confusing; if i'd have to write areas = figures.map (f) -> return f.area() at least to me, the question of "where i'm i returning from?" arises immediately.

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 void type, or an undefined value, and make procedures as a special kind of functions.

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 !-> (and it's this-binded fat relative !=>) like Coco's; or maybe some way to express "this expression's value is undefined" to put at the last expression of procedures (some kind of sigil i imagine... it should be more convenient than adding an empty return at the end).

Contributor

epidemian commented Aug 6, 2012

In the end I think removing implicit returns will cause the language to behave more as expected

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 return there.

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 areas = figures.map (f) -> f.area(). I think that forcing a return for all "valued" functions would make these kind of nested functions more confusing; if i'd have to write areas = figures.map (f) -> return f.area() at least to me, the question of "where i'm i returning from?" arises immediately.

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 void type, or an undefined value, and make procedures as a special kind of functions.

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 !-> (and it's this-binded fat relative !=>) like Coco's; or maybe some way to express "this expression's value is undefined" to put at the last expression of procedures (some kind of sigil i imagine... it should be more convenient than adding an empty return at the end).

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 6, 2012

Contributor

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.

Contributor

domenic commented Aug 6, 2012

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.

@epidemian

This comment has been minimized.

Show comment
Hide comment
@epidemian

epidemian Aug 6, 2012

Contributor

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 else cases, or add a more complex computation for the score before the result) then all of a sudden the function stops working because an explicit return is needed; there's no type-checker that'll warn me. I don't think that's very "expectable". And i'd prefer a more consistent rule for implicit returns: either always return the value of the last expression like CoffeeScript does now or always return undefined like JavaScript does; but not doing one or the other depending on the number of statements of the function.

Contributor

epidemian commented Aug 6, 2012

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 else cases, or add a more complex computation for the score before the result) then all of a sudden the function stops working because an explicit return is needed; there's no type-checker that'll warn me. I don't think that's very "expectable". And i'd prefer a more consistent rule for implicit returns: either always return the value of the last expression like CoffeeScript does now or always return undefined like JavaScript does; but not doing one or the other depending on the number of statements of the function.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 6, 2012

Contributor

@epidemian That would still be a single expression if you moved it to a second line.

Contributor

domenic commented Aug 6, 2012

@epidemian That would still be a single expression if you moved it to a second line.

@epidemian

This comment has been minimized.

Show comment
Hide comment
@epidemian

epidemian Aug 6, 2012

Contributor

@domenic Yeah, if the rule is "one expression => implicit return" then yes, changing it to a multi-line if would still work. But, what would happen if i add another expression before the "You're awesome!" inside the truth-y part of the ìf? Would the if as a whole be a single expression still?

Or if i want to add an extra calculation before the if. This would work:

endGameMessage = (score) -> 
  if score > 100
    "You're awesome!" 
  else 
    "Keep trying dude..."

But this wouldn't:

endGameMessage = (score) -> 
  totalScore = computeScorePlusBonuses score
  if totalScore > 100
    "You're awesome!" 
  else 
    "Keep trying dude..."

I think that'd complicate the semantics of the language unnecessarily.

What do you think about adding special syntax for procedures, like !-> instead of ->? (or maybe another symbol, like ->>, i don't know).

Contributor

epidemian commented Aug 6, 2012

@domenic Yeah, if the rule is "one expression => implicit return" then yes, changing it to a multi-line if would still work. But, what would happen if i add another expression before the "You're awesome!" inside the truth-y part of the ìf? Would the if as a whole be a single expression still?

Or if i want to add an extra calculation before the if. This would work:

endGameMessage = (score) -> 
  if score > 100
    "You're awesome!" 
  else 
    "Keep trying dude..."

But this wouldn't:

endGameMessage = (score) -> 
  totalScore = computeScorePlusBonuses score
  if totalScore > 100
    "You're awesome!" 
  else 
    "Keep trying dude..."

I think that'd complicate the semantics of the language unnecessarily.

What do you think about adding special syntax for procedures, like !-> instead of ->? (or maybe another symbol, like ->>, i don't know).

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Aug 6, 2012

Contributor

What do you think about adding special syntax for procedures, like !-> instead of ->? (or maybe another symbol, like ->>, i don't know).

I guess that's probably the only way forward, especially given backward compatibility. I like ->> (less reaching across the keyboard for one).

It'd be a bit annoying to switch my write-a-function muscle memory from => to =>>, and to remember to switch back to => when I want simple single-expression functions, but it's probably the best alternative.

Contributor

domenic commented Aug 6, 2012

What do you think about adding special syntax for procedures, like !-> instead of ->? (or maybe another symbol, like ->>, i don't know).

I guess that's probably the only way forward, especially given backward compatibility. I like ->> (less reaching across the keyboard for one).

It'd be a bit annoying to switch my write-a-function muscle memory from => to =>>, and to remember to switch back to => when I want simple single-expression functions, but it's probably the best alternative.

@gampleman

This comment has been minimized.

Show comment
Hide comment
@gampleman

gampleman Aug 6, 2012

Implicit returns are probably my 1. favorite feature of CoffeeScript. I like how it promotes thinking about functions in a more fundamental way rather then being just lumps of code.

I usually don't care if a function shouldn't return a value, it does anyway: then I simply don't use it. If I don't use it then typically Closure compiler will simply remove the unnecessary return. If someone uses my API and uses an undocumented return value, then they shouldn't be surprised that it breaks underneath their feet in the next version - that's why you should only use the documented stuff. The only real problem I see in this whole discussion is performance degradation when doing a loop at the end of a function and not returning anything. That's a pretty simple thing to keep in mind and IMO certainly not worth having to type return in every single function.

gampleman commented Aug 6, 2012

Implicit returns are probably my 1. favorite feature of CoffeeScript. I like how it promotes thinking about functions in a more fundamental way rather then being just lumps of code.

I usually don't care if a function shouldn't return a value, it does anyway: then I simply don't use it. If I don't use it then typically Closure compiler will simply remove the unnecessary return. If someone uses my API and uses an undocumented return value, then they shouldn't be surprised that it breaks underneath their feet in the next version - that's why you should only use the documented stuff. The only real problem I see in this whole discussion is performance degradation when doing a loop at the end of a function and not returning anything. That's a pretty simple thing to keep in mind and IMO certainly not worth having to type return in every single function.

@vendethiel

This comment has been minimized.

Show comment
Hide comment
@vendethiel

vendethiel Aug 6, 2012

Collaborator

That's not. Another example is jQuery : you should always check you're not returning false, else you may break an event or a loop.

Collaborator

vendethiel commented Aug 6, 2012

That's not. Another example is jQuery : you should always check you're not returning false, else you may break an event or a loop.

@devongovett

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 6, 2012

@epidemian @domenic For the types of functions you are talking about, I really don't mind implicit returns. Where they are a problem is when you implicitly return the result of an assignment or the results of a loop as an array. In your example, even the multiline one, you put the return value by itself at the end of the block. Why else would you put a string on a line by itself at the end of a block? If the example function had been something like this:

endGameMessage = (score) -> 
  totalScore = computeScorePlusBonuses score
  if totalScore > 100
    showMessage "You're awesome!" 
  else 
    showMessage "Keep trying dude..."

I don't think you would want to return the results of those showMessage calls implicitly, as they could be anything and break your program somewhere else. Another example would be assigning those messages to some variable. You probably usually don't want to return the result of that assignment by default as well. And it is easy to add an explicit return if you did.

I really don't think adding another function syntax is a good idea. We already have two and I think that would just confuse things.

My first solution to the problem was just to make explicit returns required everywhere. A few people seem to be objecting over the extra typing. I don't think it's that much, but the other solution would be to simply change the rules on what gets implicitly returned and what doesn't. If that is a solution people feel better about, here's the rules I'd use.

  1. Results of assignments do not get implicitly returned. You can still put a return in front of the assignment to get that behavior.
  2. Results of function calls do not get implicitly returned. You can still put a return in front of the function call to get that behavior.
  3. All other expressions and values get returned implicitly (unless someone thinks of something else that shouldn't get implicitly returned). You can still use explicit returns if you like that. :)
  4. Loops, if statements and other blocks follow the same rules above such that if the last line of the block is an assignment or function call it does not get implicitly returned directly or as an array. You can still assign the loop or if statement to a variable and return that, or just put a return in front of the if statement or loop to get the old behavior. For example, this function would return an array as usual, since the last line of the loop is not an assignment or function call:
square = (arr) ->
      for x in arr
        console.log x
        x * x

The following function would not return an array, since the last line is a function call:

each = (arr, fn) ->
      for item in arr
        fn item

If you wanted the old behavior for that last function, you could write it like this:

each = (arr, fn) ->
      ret = for item in arr
        fn item
      ret

I think this compromise would work. You would continue to get the implicit returns where you want them, and we would avoid the bugs that result from places where you don't. The only change would be that the results of assignments and function calls (and their cousins inside loops, if statements and other blocks) would not get implicitly returned, but you could still get the old behavior by adding an explicit return. What do you all think of this solution?

devongovett commented Aug 6, 2012

@epidemian @domenic For the types of functions you are talking about, I really don't mind implicit returns. Where they are a problem is when you implicitly return the result of an assignment or the results of a loop as an array. In your example, even the multiline one, you put the return value by itself at the end of the block. Why else would you put a string on a line by itself at the end of a block? If the example function had been something like this:

endGameMessage = (score) -> 
  totalScore = computeScorePlusBonuses score
  if totalScore > 100
    showMessage "You're awesome!" 
  else 
    showMessage "Keep trying dude..."

I don't think you would want to return the results of those showMessage calls implicitly, as they could be anything and break your program somewhere else. Another example would be assigning those messages to some variable. You probably usually don't want to return the result of that assignment by default as well. And it is easy to add an explicit return if you did.

I really don't think adding another function syntax is a good idea. We already have two and I think that would just confuse things.

My first solution to the problem was just to make explicit returns required everywhere. A few people seem to be objecting over the extra typing. I don't think it's that much, but the other solution would be to simply change the rules on what gets implicitly returned and what doesn't. If that is a solution people feel better about, here's the rules I'd use.

  1. Results of assignments do not get implicitly returned. You can still put a return in front of the assignment to get that behavior.
  2. Results of function calls do not get implicitly returned. You can still put a return in front of the function call to get that behavior.
  3. All other expressions and values get returned implicitly (unless someone thinks of something else that shouldn't get implicitly returned). You can still use explicit returns if you like that. :)
  4. Loops, if statements and other blocks follow the same rules above such that if the last line of the block is an assignment or function call it does not get implicitly returned directly or as an array. You can still assign the loop or if statement to a variable and return that, or just put a return in front of the if statement or loop to get the old behavior. For example, this function would return an array as usual, since the last line of the loop is not an assignment or function call:
square = (arr) ->
      for x in arr
        console.log x
        x * x

The following function would not return an array, since the last line is a function call:

each = (arr, fn) ->
      for item in arr
        fn item

If you wanted the old behavior for that last function, you could write it like this:

each = (arr, fn) ->
      ret = for item in arr
        fn item
      ret

I think this compromise would work. You would continue to get the implicit returns where you want them, and we would avoid the bugs that result from places where you don't. The only change would be that the results of assignments and function calls (and their cousins inside loops, if statements and other blocks) would not get implicitly returned, but you could still get the old behavior by adding an explicit return. What do you all think of this solution?

@epidemian

This comment has been minimized.

Show comment
Hide comment
@epidemian

epidemian Aug 6, 2012

Contributor

For the types of functions you are talking about, I really don't mind implicit returns.

The problem is: how do you differentiate those functions from the ones that would implicitly return undefined. The set of rules you propose make add some inconsistencies that you may not be aware of.

Results of assignments do not get implicitly returned. You can still put a return in front of the assignment to get that behavior.

The problem with this is that assignments are expressions in normal CoffeeScript. They can be used anywhere where an expression is valid. E.g. in another assignment:

index = total = 0

In the condition of an if statement:

if (index = str.indexOf something) isnt -1
  console.log "#{something} found at #{index}"

Really, anywhere where an expression is valid. So, to me, the expectable behaviour is that it would also be valid at the end of a function, as a other expressions, like literals or variables. A very simple refactor like memoizing the result of a function:

# Changing this...
foo = (bar) ->
  doHeavyComputation bar

# ...to this:
fooCache = {}
foo = (bar) ->
  fooCache[bar] or= doHeavyComputation bar

Would break. And finding that the problem is not in the new memoization logic, but the function simply stop returning the expected value, could be a potential PITA.

Results of function calls do not get implicitly returned. You can still put a return in front of the function call to get that behavior.

Then this original premise is not so true:

For the types of functions you are talking about, I really don't mind implicit returns.

As one of the examples i gave was areas = figures.map (f) -> f.area(). Unless you don't consider a method call the same as a function call, in which case that would be a new special case for the set of rules of implicit returns.

Also, the same argument that a function call is an expression and should be usable anywhere where an expression is valid also apply.


If the example function had been something like this:

endGameMessage = (score) -> 
  totalScore = computeScorePlusBonuses score
  if totalScore > 100
    showMessage "You're awesome!" 
  else 
    showMessage "Keep trying dude..."

If the example function would be something like that then it wouldn't really be a function. It'd be a procedure. One of the alternative proposals is to have special syntax for those cases to prevent them from returning any value.

However:

I don't think you would want to return the results of those showMessage calls implicitly, as they could be anything and break your program somewhere else.

I don't see the problem of it returning the result of showMessage. Why would it break my program somewhere else? Why would i'd be accessing the result of a function that's suposed to not return anything? In any case, it'd be an error in the caller code, not in the definition of endGameMessage.


So, in conclusion, i think adding all these rules for implicit returns is a net loss in consistency and simplicity of the language. I'd rather have implicit undefined returns always. But, as i said, i prefer the current behaviour even better :)

Contributor

epidemian commented Aug 6, 2012

For the types of functions you are talking about, I really don't mind implicit returns.

The problem is: how do you differentiate those functions from the ones that would implicitly return undefined. The set of rules you propose make add some inconsistencies that you may not be aware of.

Results of assignments do not get implicitly returned. You can still put a return in front of the assignment to get that behavior.

The problem with this is that assignments are expressions in normal CoffeeScript. They can be used anywhere where an expression is valid. E.g. in another assignment:

index = total = 0

In the condition of an if statement:

if (index = str.indexOf something) isnt -1
  console.log "#{something} found at #{index}"

Really, anywhere where an expression is valid. So, to me, the expectable behaviour is that it would also be valid at the end of a function, as a other expressions, like literals or variables. A very simple refactor like memoizing the result of a function:

# Changing this...
foo = (bar) ->
  doHeavyComputation bar

# ...to this:
fooCache = {}
foo = (bar) ->
  fooCache[bar] or= doHeavyComputation bar

Would break. And finding that the problem is not in the new memoization logic, but the function simply stop returning the expected value, could be a potential PITA.

Results of function calls do not get implicitly returned. You can still put a return in front of the function call to get that behavior.

Then this original premise is not so true:

For the types of functions you are talking about, I really don't mind implicit returns.

As one of the examples i gave was areas = figures.map (f) -> f.area(). Unless you don't consider a method call the same as a function call, in which case that would be a new special case for the set of rules of implicit returns.

Also, the same argument that a function call is an expression and should be usable anywhere where an expression is valid also apply.


If the example function had been something like this:

endGameMessage = (score) -> 
  totalScore = computeScorePlusBonuses score
  if totalScore > 100
    showMessage "You're awesome!" 
  else 
    showMessage "Keep trying dude..."

If the example function would be something like that then it wouldn't really be a function. It'd be a procedure. One of the alternative proposals is to have special syntax for those cases to prevent them from returning any value.

However:

I don't think you would want to return the results of those showMessage calls implicitly, as they could be anything and break your program somewhere else.

I don't see the problem of it returning the result of showMessage. Why would it break my program somewhere else? Why would i'd be accessing the result of a function that's suposed to not return anything? In any case, it'd be an error in the caller code, not in the definition of endGameMessage.


So, in conclusion, i think adding all these rules for implicit returns is a net loss in consistency and simplicity of the language. I'd rather have implicit undefined returns always. But, as i said, i prefer the current behaviour even better :)

@devongovett

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 6, 2012

@epidemian thanks for the response. I was worried it might be too much magic, and I too prefer requiring explicit returns always but I just wanted to see if a compromise could be made. I know that assignments and function calls are expressions, but I thought maybe these rules could help. It is probably better to have all or none.

Anyway, here's my response to what you had to say.

You could fix this example:

fooCache = {}
foo = (bar) ->
  fooCache[bar] or= doHeavyComputation bar

with an explicit return:

fooCache = {}
foo = (bar) ->
  return fooCache[bar] or= doHeavyComputation bar

Although the original function without the caching behavior would also not return anything without an explicit return under the proposed rules.

I don't see the problem of it returning the result of showMessage. Why would it break my program somewhere else? Why would i'd be accessing the result of a function that's suposed to not return anything? In any case, it'd be an error in the caller code, not in the definition of endGameMessage.

The reason it could break your program lies with the jQuery example given in the original issue. If you had a function like this:

$.each messages, ->
  doSomething this

And sometimes doSomething returned false, it would stop your loop perhaps without you knowing why. That's why implicit returns can be dangerous, especially when calling another function or assigning a variable. You end up having to code explicit returns of undefined manually all the time just to be sure your code won't break, which is really sucky and what this issue is all about.

If the example function would be something like that then it wouldn't really be a function. It'd be a procedure. One of the alternative proposals is to have special syntax for those cases to prevent them from returning any value.

In JavaScript and CoffeeScript, there is no distinction between functions and procedures. I really don't think having another yet function syntax is a good idea, and people would probably forget and use the wrong one anyway and we'd be back at square one.

So yes, I have always thought that explicit returns should be required all the time, but I was trying to come up with a compromise since people seem to be lazy enough not to want to type it out. Personally, I always use explicit returns, and I think it makes my code clearer. The compromise was an attempt to get the best of both worlds for those who like implicit returns, but it is probably better to go all explicit. I'd still like to hear what others think, however.

devongovett commented Aug 6, 2012

@epidemian thanks for the response. I was worried it might be too much magic, and I too prefer requiring explicit returns always but I just wanted to see if a compromise could be made. I know that assignments and function calls are expressions, but I thought maybe these rules could help. It is probably better to have all or none.

Anyway, here's my response to what you had to say.

You could fix this example:

fooCache = {}
foo = (bar) ->
  fooCache[bar] or= doHeavyComputation bar

with an explicit return:

fooCache = {}
foo = (bar) ->
  return fooCache[bar] or= doHeavyComputation bar

Although the original function without the caching behavior would also not return anything without an explicit return under the proposed rules.

I don't see the problem of it returning the result of showMessage. Why would it break my program somewhere else? Why would i'd be accessing the result of a function that's suposed to not return anything? In any case, it'd be an error in the caller code, not in the definition of endGameMessage.

The reason it could break your program lies with the jQuery example given in the original issue. If you had a function like this:

$.each messages, ->
  doSomething this

And sometimes doSomething returned false, it would stop your loop perhaps without you knowing why. That's why implicit returns can be dangerous, especially when calling another function or assigning a variable. You end up having to code explicit returns of undefined manually all the time just to be sure your code won't break, which is really sucky and what this issue is all about.

If the example function would be something like that then it wouldn't really be a function. It'd be a procedure. One of the alternative proposals is to have special syntax for those cases to prevent them from returning any value.

In JavaScript and CoffeeScript, there is no distinction between functions and procedures. I really don't think having another yet function syntax is a good idea, and people would probably forget and use the wrong one anyway and we'd be back at square one.

So yes, I have always thought that explicit returns should be required all the time, but I was trying to come up with a compromise since people seem to be lazy enough not to want to type it out. Personally, I always use explicit returns, and I think it makes my code clearer. The compromise was an attempt to get the best of both worlds for those who like implicit returns, but it is probably better to go all explicit. I'd still like to hear what others think, however.

@andrewrk

This comment has been minimized.

Show comment
Hide comment
@andrewrk

andrewrk Aug 6, 2012

Here's my ideal scenario:

-> / => : implicit undefined
--> / ==>: implicit last expression

Implicit last expression is longer because it occurs 1 / 5 of the time, and when you do want to use them, the code is short enough that this is still helpful.

I would still be satisfied if those were flipped.

Coco's syntax for implicit undefined unfortunately puts the suppressor far away from the -> which makes it cumbersome to type and modify. It would be nice if coco could move the ! so that you could always type !->. I've seen 3 people mistake the syntax for this which is a hint that this is how it should be anyway.

andrewrk commented Aug 6, 2012

Here's my ideal scenario:

-> / => : implicit undefined
--> / ==>: implicit last expression

Implicit last expression is longer because it occurs 1 / 5 of the time, and when you do want to use them, the code is short enough that this is still helpful.

I would still be satisfied if those were flipped.

Coco's syntax for implicit undefined unfortunately puts the suppressor far away from the -> which makes it cumbersome to type and modify. It would be nice if coco could move the ! so that you could always type !->. I've seen 3 people mistake the syntax for this which is a hint that this is how it should be anyway.

@devongovett

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Aug 6, 2012

No more function syntaxes! There is no need to have both implicit and explicit returns. It will just make the compiler more complex internally, and confuse people learning the language. It really is not a good idea. I like that people are trying to make compromises here (even I am), but introducing more syntax is not the solution to this problem in my opinion.

Implicit returns are a "nice-to-have" feature: not necessary, but they can make your life easier. Unfortunately, they also have side effects, like the bugs I have mentioned. I know this change will make your life slightly harder sometimes since you may have to type "return", but it will save you time debugging and prevent silly logic errors, while at the same time making your functions more clear and predictable as to what they are returning and exposing to the outside world.

devongovett commented Aug 6, 2012

No more function syntaxes! There is no need to have both implicit and explicit returns. It will just make the compiler more complex internally, and confuse people learning the language. It really is not a good idea. I like that people are trying to make compromises here (even I am), but introducing more syntax is not the solution to this problem in my opinion.

Implicit returns are a "nice-to-have" feature: not necessary, but they can make your life easier. Unfortunately, they also have side effects, like the bugs I have mentioned. I know this change will make your life slightly harder sometimes since you may have to type "return", but it will save you time debugging and prevent silly logic errors, while at the same time making your functions more clear and predictable as to what they are returning and exposing to the outside world.

@Zyphrax

This comment has been minimized.

Show comment
Hide comment
@Zyphrax

Zyphrax Nov 3, 2014

The implicit returns are reallllly annoying.

When I'm writing in CoffeeScript for my Angular projects I really have to make sure that I add "return" to some of my functions, otherwise I'll spend the rest of the day going on a frustrating bughunt.

I would suggest:

  • One line statements will result in an implicit return
  • Multi-line statements, no implicit return

Or make <- an alias for return statements and drop implicit returns.

Regardless it would be great to have a compiler option to disable implicit returns. I'd rather disable it and write "return" when I have to, instead of having all of these useless return statements in my code.

Zyphrax commented Nov 3, 2014

The implicit returns are reallllly annoying.

When I'm writing in CoffeeScript for my Angular projects I really have to make sure that I add "return" to some of my functions, otherwise I'll spend the rest of the day going on a frustrating bughunt.

I would suggest:

  • One line statements will result in an implicit return
  • Multi-line statements, no implicit return

Or make <- an alias for return statements and drop implicit returns.

Regardless it would be great to have a compiler option to disable implicit returns. I'd rather disable it and write "return" when I have to, instead of having all of these useless return statements in my code.

@carlsmith

This comment has been minimized.

Show comment
Hide comment
@carlsmith

carlsmith Nov 3, 2014

Contributor

If you're putting return statements at the end of lots of functions, you're doing something unusual. If you don't want the return value, don't assign it to anything. It only matters if the last value evaluated is exceptionally large and you don't want to keep it.

Implicit returns also flow naturally with conditionals as expressions and so on. Removing one, would make other things less consistent.

gt = (x, y) -> if x > y then true else false

JavaScript already has implicit returns too. It just returns undefined instead. The suggested syntax for setting a default return value is just sugar for something that's sweet enough already.

Contributor

carlsmith commented Nov 3, 2014

If you're putting return statements at the end of lots of functions, you're doing something unusual. If you don't want the return value, don't assign it to anything. It only matters if the last value evaluated is exceptionally large and you don't want to keep it.

Implicit returns also flow naturally with conditionals as expressions and so on. Removing one, would make other things less consistent.

gt = (x, y) -> if x > y then true else false

JavaScript already has implicit returns too. It just returns undefined instead. The suggested syntax for setting a default return value is just sugar for something that's sweet enough already.

@crissdev

This comment has been minimized.

Show comment
Hide comment
@crissdev

crissdev Nov 3, 2014

@carlsmith Some libraries really care about the return value. I would say the most trivial example is by using the [jQuery.each](We can break the $.each%28%29 loop at a particular iteration by making the callback function return false) function. For more advanced bugs 😆 implicitly return a promise when you don't want to. CoffeScript remains a great choise if you can't develop with ES6.

crissdev commented Nov 3, 2014

@carlsmith Some libraries really care about the return value. I would say the most trivial example is by using the [jQuery.each](We can break the $.each%28%29 loop at a particular iteration by making the callback function return false) function. For more advanced bugs 😆 implicitly return a promise when you don't want to. CoffeScript remains a great choise if you can't develop with ES6.

@carlsmith

This comment has been minimized.

Show comment
Hide comment
@carlsmith

carlsmith Nov 3, 2014

Contributor

@crissdev - Yeah, you're right; there's more than just very large return values to worry about. I've not used promises, so that didn't occur to me. Still, the idea that...

f = true (x) -> return false if x

...is better than...

f = (x) -> if x then false else true

...isn't much of a sales pitch.

Not having a way to make a function implicitly return undefined seems to be a small, but significant and growing annoyance. Having a syntax for setting the default value would solve that problem, but is a whole new feature to fix an issue with an existing one. IMO unless there's a strong case for the new feature, we should just fix the existing one, which isn't actually broken at all, just awkward at times.

Contributor

carlsmith commented Nov 3, 2014

@crissdev - Yeah, you're right; there's more than just very large return values to worry about. I've not used promises, so that didn't occur to me. Still, the idea that...

f = true (x) -> return false if x

...is better than...

f = (x) -> if x then false else true

...isn't much of a sales pitch.

Not having a way to make a function implicitly return undefined seems to be a small, but significant and growing annoyance. Having a syntax for setting the default value would solve that problem, but is a whole new feature to fix an issue with an existing one. IMO unless there's a strong case for the new feature, we should just fix the existing one, which isn't actually broken at all, just awkward at times.

@Zyphrax

This comment has been minimized.

Show comment
Hide comment
@Zyphrax

Zyphrax Nov 3, 2014

@carlsmith when working with Angular nearly everything is wrapped in functions. In many cases the implicit returns don't make sense and even cause Angular to break.

I think that CoffeeScript is great and it simplifies / reduces a lot of javascript. But this is really bugging me. Perhaps it is time to check out Traceur :). ES6 seems to fix a lot of javascript frustrations (I guess my no 1 is having to type "function" all the time and the weird for-each loops)

Zyphrax commented Nov 3, 2014

@carlsmith when working with Angular nearly everything is wrapped in functions. In many cases the implicit returns don't make sense and even cause Angular to break.

I think that CoffeeScript is great and it simplifies / reduces a lot of javascript. But this is really bugging me. Perhaps it is time to check out Traceur :). ES6 seems to fix a lot of javascript frustrations (I guess my no 1 is having to type "function" all the time and the weird for-each loops)

@michaelficarra

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Nov 3, 2014

Collaborator

I'm going to lock this issue, as there's no reason to have a discussion about this -- it's not going to change. If you want to write procedural code, don't choose to use a language where (nearly) everything is an expression. In CoffeeScript code, you are meant to be working with values much more frequently than you work with side effects. If that's not the case, sorry, it's going to be a burden. Use the right tool for the job.

Collaborator

michaelficarra commented Nov 3, 2014

I'm going to lock this issue, as there's no reason to have a discussion about this -- it's not going to change. If you want to write procedural code, don't choose to use a language where (nearly) everything is an expression. In CoffeeScript code, you are meant to be working with values much more frequently than you work with side effects. If that's not the case, sorry, it's going to be a burden. Use the right tool for the job.

Repository owner locked and limited conversation to collaborators Nov 3, 2014

@jashkenas

This comment has been minimized.

Show comment
Hide comment
@jashkenas

jashkenas Nov 3, 2014

Owner

I agree that this is never going to change — except in your very own fork of CoffeeScript ... but let's not lock issues on here. Folks should be able to keep on talking about stuff as long as they care to. Otherwise, we're just going to end up with a whole bunch more duplicated issues being created and locked and scattered to the wind.

Owner

jashkenas commented Nov 3, 2014

I agree that this is never going to change — except in your very own fork of CoffeeScript ... but let's not lock issues on here. Folks should be able to keep on talking about stuff as long as they care to. Otherwise, we're just going to end up with a whole bunch more duplicated issues being created and locked and scattered to the wind.

Repository owner unlocked this conversation Nov 3, 2014

@epidemian

This comment has been minimized.

Show comment
Hide comment
@epidemian

epidemian Nov 3, 2014

Contributor

Thanks for keeping the discussion open, @jashkenas.

I'd like to ask some open-ended questions to (hopefully) move the discussion into other directions:

Why is this such a problem with CoffeeScript?

Why is this not a problem in Ruby, where method/block invocations also evaluate to their last expression?

Is this mostly a case of people coming from different programming languages (e.g. JavaScript, Python) having different expectations? Or is the assumption that most functions should evaluate to something meaningful ill-suited for the environment where CoffeeScript is used (i.e. browsers, node)? If the last is true, can we do something to remedy that?

Contributor

epidemian commented Nov 3, 2014

Thanks for keeping the discussion open, @jashkenas.

I'd like to ask some open-ended questions to (hopefully) move the discussion into other directions:

Why is this such a problem with CoffeeScript?

Why is this not a problem in Ruby, where method/block invocations also evaluate to their last expression?

Is this mostly a case of people coming from different programming languages (e.g. JavaScript, Python) having different expectations? Or is the assumption that most functions should evaluate to something meaningful ill-suited for the environment where CoffeeScript is used (i.e. browsers, node)? If the last is true, can we do something to remedy that?

@jashkenas

This comment has been minimized.

Show comment
Hide comment
@jashkenas

jashkenas Nov 3, 2014

Owner

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.

Owner

jashkenas commented Nov 3, 2014

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

This comment has been minimized.

Show comment
Hide comment
@devongovett

devongovett Nov 3, 2014

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.

devongovett commented Nov 3, 2014

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

This comment has been minimized.

Show comment
Hide comment
@crissdev

crissdev commented Nov 3, 2014

@devongovett Great work 👍

@Zyphrax

This comment has been minimized.

Show comment
Hide comment
@Zyphrax

Zyphrax Nov 3, 2014

"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 :)

Zyphrax commented Nov 3, 2014

"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

This comment has been minimized.

Show comment
Hide comment
@demisx

demisx Nov 3, 2014

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

demisx commented Nov 3, 2014

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

@sedabull

This comment has been minimized.

Show comment
Hide comment
@sedabull

sedabull Nov 3, 2014

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 👍 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:
#2477 (comment)

sedabull commented Nov 3, 2014

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 👍 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:
#2477 (comment)

@carlsmith

This comment has been minimized.

Show comment
Hide comment
@carlsmith

carlsmith Nov 4, 2014

Contributor

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.

Contributor

carlsmith commented Nov 4, 2014

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.

@Zyphrax

This comment has been minimized.

Show comment
Hide comment
@Zyphrax

Zyphrax Nov 4, 2014

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

Zyphrax commented Nov 4, 2014

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

@jbarker4682

This comment has been minimized.

Show comment
Hide comment
@jbarker4682

jbarker4682 Nov 4, 2014

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.

jbarker4682 commented Nov 4, 2014

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

This comment has been minimized.

Show comment
Hide comment
@carlsmith

carlsmith Nov 5, 2014

Contributor

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

Contributor

carlsmith commented Nov 5, 2014

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

@Zyphrax

This comment has been minimized.

Show comment
Hide comment
@Zyphrax

Zyphrax Nov 5, 2014

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

Zyphrax commented Nov 5, 2014

@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

This comment has been minimized.

Show comment
Hide comment
@macgyver

macgyver Feb 17, 2015

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?

macgyver commented Feb 17, 2015

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

This comment has been minimized.

Show comment
Hide comment
@vendethiel

vendethiel Feb 17, 2015

Collaborator

Yes, or a loop in general.

Collaborator

vendethiel commented Feb 17, 2015

Yes, or a loop in general.

@macgyver

This comment has been minimized.

Show comment
Hide comment
@macgyver

macgyver Feb 17, 2015

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.

macgyver commented Feb 17, 2015

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

This comment has been minimized.

Show comment
Hide comment
@vendethiel

vendethiel Feb 17, 2015

Collaborator

I mean "loop" by "comprehension".

Collaborator

vendethiel commented Feb 17, 2015

I mean "loop" by "comprehension".

@macgyver

This comment has been minimized.

Show comment
Hide comment
@macgyver

macgyver Feb 17, 2015

Ah.. I see now - thank you!

macgyver commented Feb 17, 2015

Ah.. I see now - thank you!

@odraencoded

This comment has been minimized.

Show comment
Hide comment
@odraencoded

odraencoded May 28, 2015

@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 commented May 28, 2015

@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

This comment has been minimized.

Show comment
Hide comment
@macgyver

macgyver May 28, 2015

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

macgyver commented May 28, 2015

@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

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra May 28, 2015

Collaborator

@odraencoded what programming style?

Collaborator

michaelficarra commented May 28, 2015

@odraencoded what programming style?

@git2samus

This comment has been minimized.

Show comment
Hide comment
@git2samus

git2samus Oct 26, 2015

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.

git2samus commented Oct 26, 2015

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

This comment has been minimized.

Show comment
Hide comment
@krisnye

krisnye May 31, 2016

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 May 31, 2016

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

This comment has been minimized.

Show comment
Hide comment
@krisnye

krisnye May 31, 2016

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

krisnye commented May 31, 2016

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment