-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IDEA] "returning" #332
Comments
wow, I've never seen that before. Looks like it's implemented by activesupport as an extention to Object, rather than an actual language thing. Which makes your middle snippet the equivalent (without requiring any language changes). It doesn't seem that ugly to me... Personally, I don't see the benefit - but others may disagree |
You could probably drop the If this was a part of the language I would probably use it, but I agree with gfxmonk in that it's not really a substantial enough benefit to bother including it because you could just write what it actually means without much more code. |
I'm also not a fan of So, here's my case against I would argue that most places where returning is used:
Are a case of feature envy. That list of stateful transformations should be encapsulated within the car itself, like so:
So, for a feature that inverts the expected code flow, and is designed to enable poor patterns of building up state, I think this ticket is a |
“Get of object from some function; do something else with it; return it” is a very general pattern found in nearly every piece of code. Say you're rendering a node:
Or maybe you want to use a size and return it at the same moment:
Or maybe you want to write a function that wraps another function and adds to its behaviour:
Note that in most of these examples the returned value wasn't even being mutated, so the concern of encouraging mutator chains does not apply to my use cases (my bad I used push() to illustrate). Now, writing “return node” or “node” at the end of a function strikes me as ugly and non-CoffeeScriptish. Maybe you don't often do that — good for you then, but it craps up my code often enough to be willing to cook a patch for this. Now for “not really a substantial enough benefit” — isn't the goal of CoffeeScript to add little things that make coding more fun? And I guess err.the_blog makes a good point for usage of “returning” even in mutator scenarios (http://errtheblog.com/posts/27-quickly-returning), like this:
While sometimes your code benefits from defining an updateStateAndSave method on an object, sometimes it does not, and for those cases “returning” allows the code to stay clean. I agree that in CoffeeScript it makes sense to just call this “as”. |
Note that this could just be a variant of JavaScript's “let” statement: https://developer.mozilla.org/en/New_in_JavaScript_1.7#Block_scope_with_let Say if we support “let”, and its value is the value of the last assigned expression, does that sound like a better plan to you? |
andreyvit: We're discussing this on From the discusson on
|
My samples rewritten with the proposed syntax:
I'm quite happy with it. Too bad it seems to give you a hard time updating the grammar. Note that you can ditch single-liners w.r.t. to this feature — i.e. only look for (ident-list) NEWLINE. |
I still think it's mad introducing more syntax rules to learn, when the purpose of the vanilla coffee-script version is clearer, and almost exactly as verbose (maybe an additional line, maybe not) |
After a bit more discussion, this enhancement seems like something we should either go one way or the other on. Named return values in the function signature should either be required or left out, and I don't think we can justify a forced use. If this were a statically-typed language where we had to define the return type every function in the first place, it would be different. So, closing the ticket as a
And then use:
|
Out of interest - what was the reasoning behind "Named return values in the function signature should either be required or left out". Consistency throughout the language ? |
I pointed out that introducing them (but having them optional) would mean there are multiple places you need to look in order to figure out what's being returned - each return statement, and the function header. I think it should be one or the other, otherwise it gets confusing. |
…AND Jash wanted to feel good about zero open tickets. kidding Agreed, when you have to look at each return statement (esp. that one buried 3 indentation levels deep), looking at the function header makes quite a difference. While at it, why don't we disallow multiple “return” statements to make figuring the return value even easier? Now seriously. Named return values was going to be one of the beautiful twists of the language, documenting all those functions that return something which is not quite clear from the name (like find() returning a zeroBasedIndex). And you kill it why? Because it makes 0.1% more difficult to look up return values. Mind you, it makes figuring the return value unnecessary in many cases, since it's documented right in the header. |
It does sounds pretty neat. I sometimes feel a bit wrong when I'm returning values from a function in what feels like a dirty way. It's probably one of those features that we'd all have to try in real coding to get a true idea if it was useful or not. Here's a gist of some code that I'm actually working on with before/after return headers: |
I have several concerns about this. But first, in my time working on Coffee I've learnt to accept the fact not every little (or big) feature is core material. Everyone is entitled to their opinion and as a community we have to respect that and just deal with it. Now onto my concerns: if we adopt the header, what happens if we Do we completely drop the How about functions that return a value from multiple places? A trivial example:
I don't consider the rewrite better, do you? If we adopt headers, how do we stop, i.e., exit a function? Think the above example with several nesting levels and We also have to consider one-liners, those lovely short functions that just make sense:
Also take a look at weepy's example. I may be wrong, but it saved him three lines of code in total? I am still living in Coffee land and I find the reworked copy confusing as I expect the last line to be returned ..
I admit, I like what headers can bring to the language, but without solving the issues it would create first, it is not ready for prime time. |
Here's my vision on this: named return values are meant for those functions that look better with them, and not meant for all other ones. The functions that benefit from named return values likely have just one return at the end of the function, and look more or less like this:
I'm okay if we completely ban “return” statements inside such functions, if that helps to get the feature landed. However, I can easily imagine the following being useful either:
So I'd vote for not banning returns, and just have them override the implicit return values. But I would be just as happy with banned returns. To all those guys who worry about looking for return statements in a function: just don't use named return values for obscure functions. Again, this feature (like any other one actually) is to be used only when it makes the code better and clearer. I guess this falls under the general use/abuse discussion, though I believed those who want to see abusable features banned usually stick to statically typed languages. The key is that you really don't have to abuse named return values (or destructuring assignments, or statements-are-expressions, or dynamic typing, or lambdas). What about adding this as a provisional feature, without any promises for backwards compatibility, and give it a try? (Though CoffeeScript's home page makes it clear that the whole language is provisional.) |
Ah, but just as the language itself is provisional, so is the "closed" state of this ticket. Re-opening it because there's still plenty of discussion. |
I worry about all too many provisional features, because it's hard to take them out if someone (even a vocal minority) like them.
Every feature adds to the surface area of the language. A larger surface area means more you need to hold in your head to read code, and more things to learn (and forget) when you're just starting out. If, while learning the language, you miss some part of it, then you could get completely stumped by this. Especially if it's a syntax-only thing like this - no keywords to google or look for in documentation. Perl says "there's more than one way to do it". Python says "There should be one-- and preferably only one --obvious way to do it". I'm obviously in the python camp. I think ruby and perl have far too many odd constructs and "beautiful twists", as you put it (I do not want to deal with a twisty programming language). I believe this construct adds very little to expressivity or conciseness, and adds the potential (however minor or trivial it seems in discussion) for confusion to one of the most important parts of a function to be able to understand at a glance - what it's actually giving you! |
It's a pretty easy concept to learn. Granted it won't save many lines of code, but once you understand the idea, it makes quite a few scenarios easier to understand. |
Oh yeah, now it makes sense: I'm definitely in the Ruby camp here. I think I'll spare the arguments (for “more than one way to do it”), since the discussion obviously can't lead to an agreement. So we need to find a way to agree without really agreeing. My view of the facts is:
Now some judgmental claims. If we were talking about adding generics, or type inferencing type system, or actor-model concurrency, it would make perfect sense to talk about the surface area, and being harder to learn etc. But seriously, a named return value? That's totally trivial. The docs can describe it in two sentences. That's not something that gives you a headache for keeping it in your head. Man, limitations of discriminant constraints of anonymous access types in Ada — that's something that's hard to keep in mind (AND learn). Names for return values — not worth an argument about learnability. |
@andreyvit: on 7. and 8. -- what I would do -- I want something badly. I add it to my own branch/fork. I let the community and Jeremy decide if it's core material. If it's not, hey life goes on. I keep on using it nevertheless. Stan is a happy bunny.
Sounds like a plan, LOL. EDIT:
Could I just say I don't like them? Consider this whining. |
You haven't experienced the community response to the half operators, then. I feel like the decision on this one is going to be an indicator for future development for the language, so it's important that it meets the spirit of the language. The community seems fairly equally split on the issue, as do the arguments for and against. Unfortunately for the group who are trying to get it into the language, it seems like most of the current contributors to the code base are against it or are on the fence. How many lines of code it saves seems to have come up a lot, but I think that's almost irrelevant. What's important here is clarity, and that's what the arguments should, and have for the most part, focus on. In one camp (and the original viewpoint when the idea was originally discussed) is that it defines a return value immediately, making it very clear what your intentions are with that value throughout the rest of the function. In the other, there's the fact that a There's also the case of whether it should be compulsory for every function. I think this is a terrible idea illustrated in some examples above, as we lose the ability to define lamba-like functions. Making it compulsory for block functions makes more sense - you're defining a lambda function with side effects. Rather than defining simply return values, you might even write this: Maybe my brief proposal above makes more sense as an optional part of the language, with that kind losing the ability to return a different value? I'm not really concerned, as long as a decision is not made too hastily, either for or against. I like the idea, and I'd hate for the final decision to negatively affect the rest of Coffeescript's development. Like Stan said, build it for yourself, try it out, and come back with the implementation. |
Talking with Jash more about this, we came up with more cases where this feature helps in case it is allowed.
Not sure all of these use cases should be supported (to me the “undefined” one looks compelling, while “true” one looks nice but not too important), just an idea. |
Good examples -- however still unclear about short lambdas, e.g.,
|
I think I'm entirely for allowing only valueless return's in functions with named parameters:
This works for quick checks when you want to return a falsy value:
but then you can't forget to return something that you've promised:
|
StanAngeloff: you just don't use this in lambdas. |
For the record, I'm not saying we can't think of some better syntax for this. |
surely the rule is simple: named returns look like a variable name in parentheses after the |
andreyvit and I had a nice long chat about this on #coffeescript. I'm closing this ticket again as a The great appeal of this proposal lies in the function signature, the possibility that at a glance you would be able to read the inputs and the outputs of a function. But upon closer inspection, it's a false appeal. Reading through a typical CoffeeScript function begins with the function name, followed by a list of inputs to the function, and then the function body, and finally, at the end of the function body, the output value. This is the proper order in which to read a function -- you need to know the inputs in order to understand the body, and you need to read the body in order to understand what the result value is. The proposal in this ticket names the result value, but you have no basis for knowing anything about the value without reading the method body, and, within the method body, you have no way of knowing at what point you can stop reading, and the point after which the rest is just side effects. With the current way of doing things, there's no such problem. Everything until the end of the method is always relevant to the return value, and the last line of the function is always the return value, whether returned explicitly or not. This is far better, I think, then having to scan through the function body to find the last line that mentions the name in the signature, and then tracing backwards from there. So, if we were to re-invent this named function result idea from first principles, I'd prefer to see it laid out in the proper order: |
It appears I'm very late to the party. But I tend to agree with jashkenas here. I favor rigor and consistency even in my high-level / dynamic / scripting languages. And that's the sense I get from phrases like "the proper order in which to read a function" and "from first principles." This feature seems like it would be a convenience for a number of people. But I do not see it as permitting us to write code at a higher level than before. Like jashkenas, I see this as causing more confusion than clarification. Conveniences in good languages are well-founded and well-grounded. And just like Steve Jobs does, a language designer must say "no" to a thousand good ideas in order to make room for and say "yes" to the next killer idea. As a footnote, Haskell permits the style of programming wished for in this ticket:
So the feature is interesting and useful when it comes to Haskell, because program flow in Haskell is not linear, not |
Just wanted to pipe in to say that There was a brief moment in time where us newcomers, from PHP and Java and the like, thought, "Wow, look at the cool things Ruby can do!" So we used We don't define normal methods with I currently have 207 RubyGems installed and only two (2) of them use Disclaimer: I wrote the referenced Err the Blog post on |
A well-known Ruby idiom is to replace
with
While this can be expressed as a custom function, the resulting solution looks ugly. What about adding a "returning" directly into CoffeeScript? Like this:
Question is, would the author want to accept a patch for this?
The text was updated successfully, but these errors were encountered: