Skip to content
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 · 127 comments
Closed

Remove implicit returns? #2477

devongovett opened this issue Aug 4, 2012 · 127 comments

Comments

@devongovett
Copy link

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
Copy link

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
Copy link

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
Copy link
Author

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
Copy link

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
Copy link
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

@michaelficarra
Copy link
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.

@jussi-kalliokoski
Copy link

void ALL THE THINGS. ;)

@devongovett
Copy link
Author

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
Copy link

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

@michaelficarra
Copy link
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.

@andrewrk
Copy link

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
Copy link
Author

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
Copy link

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
Copy link
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.

@devongovett
Copy link
Author

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
Copy link
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.

@devongovett
Copy link
Author

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
Copy link

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
Copy link
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
Copy link
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.

@jussi-kalliokoski
Copy link

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
Copy link
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".

@jussi-kalliokoski
Copy link

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
Copy link
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?

@jussi-kalliokoski
Copy link

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
Copy link

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
Copy link

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

@domenic
Copy link
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
Copy link
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.

@carlsmith
Copy link
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.

@yvbeek
Copy link

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

@ghost
Copy link

ghost 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
Copy link
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.

@yvbeek
Copy link

yvbeek 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
Copy link

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
Copy link
Collaborator

Yes, or a loop in general.

@macgyver
Copy link

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
Copy link
Collaborator

I mean "loop" by "comprehension".

@macgyver
Copy link

Ah.. I see now - thank you!

@odraencoded
Copy link

@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
Copy link

@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
Copy link
Collaborator

@odraencoded what programming style?

@git2samus
Copy link

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
Copy link

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
Copy link

krisnye commented May 31, 2016

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

@AustinBryan
Copy link

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

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

@Inve1951
Copy link
Contributor

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

@carlsmith
Copy link
Contributor

@krisnye - Using the arrow operator exclusively for single-expression lambdas with implicit returns (and a different syntax for functions with blocks and explicit returns) is a nice compromise. I've argued for the same thing on here in the past, but to offer a counter...

CoffeeScript (unlike Python et al) allows blocks (with significant indentation) to be nested inside expressions. The blocks are expressions themselves. Apart from control flow statements, everything is an expression. If the language generally aims to make all blocks implicitly evaluate to value of the last subexpression, you would definitely expect that to be true for functions.

Does your language evaluate branches and loops still, just not functions? Are any blocks expressions?

@krisnye
Copy link

krisnye commented Nov 13, 2018

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

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

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

Div()
    for name, task of tasks
        Div()
            task.name
            if task.complete
                Button()
                     "Remove"

@carlsmith
Copy link
Contributor

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

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

I like restricting the arrow operator to Pythonic lambdas, generally, but for CoffeeScript (where blocks are expressions, and can be significantly indented as subexpressions), there's a consistency-based argument for the current approach.

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

No branches or pull requests