Closed rattrayalex closed 8 years ago
The one thing that has always driven me crazy is not being able to modify the incremented variable in a for loop. eg, If I want to skip an iteration.
for name, i in names
i++
i++
would be expected to skip an iteration, as it assumed that i
will automatically be increased in this for loop.
Thoughts?
Sorry, but that looks kind of bad and poorly readable @ozjd - the loop variable should be changed in the head of the loop only - changing it in the body as well will just make it completely unreadable. Add a "continue", or make the loop skip every second entry by doing it in the loop somehow (like "for name, i in names by 2).
If you have access to the iteration counter, it seems intuitive to be able to modify the iteration.
In the end, I created a variable that's checked on each iteration, and skips over the loop as needed.
In the case of any syntax similar to for x, y in {key: value, ...}
, this doesn't make any sense. The syntax is basically an implicit map function invoking the expression you give it. If it does work, I would highly recommend against using this. This is a fast lane to hard-to-debug bugs.
It would be possible and is used heavily in for(i = x; i < y; i += 2) { i -= 1;}
constructs, but we do not have those in CS as far as I can tell.
Additionally, you might want to use a reduce/fold function instead where you use the accumulator as arbitrary counting variable.
As discussed at length in #1, CoffeeScript uses nonlocal assignments by default, which is unusual and confusing.
Make it a normal array (if it's actually referenced).
CoffeeScript operator precedence makes a = b for b in c
equal to (a = b) for b in c
, so a
gets reassigned on every iteration. You normally want to do a = (b for b in c)
, so a
is assigned once, to an array of the values from each iteration. We should require array comprehensions to be wrapped in brackets: a = [ b for b in c ]
.
@carlsmith wow, awesome list!!
Many things seem more like feature requests than gripes, however. I just created a "New Feature Ideas" thread โย https://github.com/coffeescript6/discuss/issues/16 โย any chance you'd be willing to break out the feature requests to that one?
For example, I personally love the "Automatically Reference First Args" idea, but it's definitely not a bug in coffeescript that the feature doesn't exist yet.
@rattrayalex - Done. No worries.
If you have access to the iteration counter, it seems intuitive to be able to modify the iteration.
If we're talking about iteration specifically, it pretty directly refers to executing some subroutine for each item in some sequence, so there's an expectation that it's not going to jump around. Still you're right, there's a need to manually advance the iteration counter sometimes. In a lexer for example, you want to iterate over the chars in the source, but if you use a big for char in source
loop, you find you need to skip ahead to pop the characters off as you process each token. In those cases, it's best to scrap any pretence of being an iteration, and just use a while-loop that spins forever, then keep track of the character index manually and break manually when you're done. Basically, if it's not a proper iteration, just fall back to a good old fashioned loop and counter. Obviously, it's perfectly fine to use continue
in a for-loop, but that doesn't solve the problem you hinted at.
There are a few gripes I'd like to throw in here myself, but I'll pare it down to just one here...
No. Just no. I understand the utility of implicit returns for visual clarity in small methods.
class Data
constructor: (@value) ->
getValue: -> @value
and at times, when iterating or filtering arrays and returning the result, but returning undocumented or non-obvious values ad hoc as a result of brevity...? I could be alone in this. But no. Implicit return must die in a new edition of the language. If there is going to be an implicit return, do what ES does and return undefined!
Theres a whole spate of backwards compatibility issues of course, but I think a partial solution could possibly be for this languages compiler to take standard CS in as an input, and to transpile that to CS6 before transpiling to the end language - ES4/5/6/7...
We must implicitly return something, as a function call is an expression. It could be undefined
though. I would rather have it so that functions that have a single expression as their body return its value implicitly, but functions with more than one expression must return something explicitly, else undefined
.
This has been discussed before, and a few of us wanted that compromise.
What do you save by getting rid of implicit returns? You would have to type return a heck of a lot more often if you get rid of it.
The argument, which I personally don't care about much, except because other people care about it a lot, is that the return values can be large, and there's a perceived inefficiency in generating and returning big chunks of stuff when it's immediately garbage collected. We could maybe be more intelligent about it at the compiler level, eliminating implicit returns wherever we are certain they go unused. The argument seems to be purely based on perf, so eliminating the inefficiencies would help solve the issue, without changing the language, as I understand it. Again, it's something I only worry about because a lot of other people hate the feature, so I'm not the best person to judge solutions.
I don't think it's possible to decide return statements on a compiler level. If you are creating a library, how are you to know if the return statement will be used or not if the user script isn't available?
Btw, at work we have used CoffeeScript on slow set top boxes for years, boxes with max 2MB JS memory heap until stuff gets garbage collected. You can definitely say that we have had a close eye on memory allocation with proper profilers for years, and CS has never created trouble for us. Even for us, functions as expressions is a positive and has never given us problems.
I don't think it's possible to decide return statements on a compiler level. If you are creating a library, how are you to know if the return statement will be used or not if the user script isn't available?
I don't understand. It's only syntactic, and only matters to the author of the function. When you call a function, you never know how it returned a value. It doesn't matter.
Btw, at work we have used CoffeeScript on slow set top boxes for years, boxes with max 2MB JS memory heap until stuff gets garbage collected. You can definitely say that we have had a close eye on memory allocation with proper profilers for years, and CS has never created trouble for us. Even for us, functions as expressions is a positive and has never given us problems.
That's really interesting. Browsers spend very little time executing JS. I assume it's similar with set top boxes. Pure JS stuff, like the CoffeeScript compiler seem fine as well. I've never seen a concrete example of implicit returns being a problem, but a lot of people want a way to disable it (per file or per function with special operators).
I don't think it's possible to decide return statements on a compiler level. If you are creating a library, how are you to know if the return statement will be used or not if the user script isn't available?
I don't understand. It's only syntactic, and only matters to the author of the function. When you call a function, you never know how it returned a value. It doesn't matter.
Sorry. I was thinking of having implicit returns in functions that only contain one expression. When it comes to only imlipicitly returning things if they're used, you're right. I don't know what I was thinking, but yeah, it wouldn't work.
That's really interesting. Browsers spend very little time executing JS. I assume it's similar with set top boxes. Pure JS stuff, like the CoffeeScript compiler seem fine as well. I've never seen a concrete example of implicit returns being a problem, but a lot of people want a way to disable it (per file or per function with special operators).
It is passed by reference anyway, not as a copy. And the return statement won't stay in memory unless you decide to save it on purpose when you call the function.
@carlsmith
This has been discussed before
Can you provide a link?
Removing implicit returns would present backwards-incompatibility pains of astonishing proportions (regardless of whether they're a good idea in general or not).
Personally, I ported the entire frontend codebase at work from coffeescript to es6 several months ago, and removing return statements that were added implicitly, but never used, was the biggest pain I had to deal with. So I do agree that they're a problem.
@objectkit what do you think of a way of silencing implicit returns? Something like -%>
(ideally we'd find something prettier).
Either way, @objectkit would you like to open a new thread to discuss implicit returns?
Can you provide a link?
This has been discussed a bunch of times. Here's some examples; there are more:
Wow, some really terrific discussion there. Thanks for digging those up @carlsmith !
No problem mate. It's worth gathering these lists together. We should maybe put them in the wiki so everyone can edit them, and start collating all the previous discussions from CS, Redux, Coco etc. that are relevant and valuable. It'd be a good resource for all the CoffeeScript family of projects, whenever anyone needs to see what's been said in the past about a feature. Issues is a good place to discuss stuff, but GH wikis are really cool as well for the right content.
If you create a wiki and open it up to the public, I'll make a page for Implicit Return, with a quick summary of the feature, and the start of a list of previous discussion. I'll look on Google to see if Ashkenas or anyone with interesting views has blogged about it or whatever too.
The wiki would be good for documenting ES6 features, just covering the stuff that's particular to CoffeeScript, linking to the ES6 docs, and summarising our project's current consensus on the feature once there is one etc.
Great idea! https://github.com/coffeescript6/discuss/wiki let me know if you have any challenges
Hello all - I feel it looks as if I just threw a grenade full of my own linguistic biases into a conversation then fecked off over the hill! Not so - my time is pretty broken up and I meant to get back here much sooner that this. Apologies!
Anyway, great to see feedback and the new wiki entry! @rattrayalex - sure, I'll give silent implicit return syntax a look
I think at this point it behooves us to open a stand-alone issue for implicit return and move the discussion there?
Side note - nice work @carlsmith on the wiki page and @rattrayalex for starting all of this cs6 discussion. I'm very excited about whats happening here.
I think one of the things I constantly am surprised isn't there is the ternary operator. (bool)?true:false
While I understand the idea behind having the code be more readable, it is still an exceptionally useful construct. Breaking things into multiple line if statements in this case is always a pain.
@mrmowgli: You could always do a one liner like this.
result = if condition then true else false
Or just
result = condition is true
Or even
result = condition
Or even
condition
;)
Just wanted to add regarding the implicit return, it bit me once when writing some tasks for Gulp where Gulp would treat a task returning truthy as having run, versus returning void
(like a JavaScript function without a return
) would be treated like a promise. (Or something to this effect, it was awhile ago, I donโt remember exactly.)
Anyway I solved it by just putting return
on its own line at the end:
module.exports = (gulp, plugins, config) ->
(done) ->
# Get latest git commit log, save into an environment variable for Browserify/envify to insert into our app
plugins.git.exec
args: "log -1 --pretty=format:\"{%n \\\"commit\\\": \\\"%H\\\",%n \\\"author\\\": \\\"%an\\\",%n \\\"authorEmail\\\": \\\"%ae\\\",%n \\\"date\\\": \\\"%ad\\\",%n \\\"message\\\": \\\"%s\\\"%n}\" --date=iso8601"
quiet: yes
, (err, stdout) ->
if err then throw err
# Escape any quotes that might be in the commit message
find = stdout.match(/\n "message": "(.*)"\n}/m)[1]
replace = find.replace /"/g, '\\"'
stdout = stdout.replace find, replace
# Save and output
process.env.COMMIT = stdout
config.commit = JSON.parse stdout
plugins.util.log "Building commit #{config.commit.commit}: " + plugins.util.colors.yellow config.commit.message
done()
return
This is hardly a terrible workaround. You could also just put no
or null
on its own on the last line, if youโd rather return a falsy value than โvoidโ.
That implicit return
workaround became convention in our team:
No Implicit Returns: Always explicitly return in functions with more than one statement
Which kind of defeats the purpose of an implicit return.
Some examples that forced us to create that rule:
Implicit returns are consistent with loops being array comprehensions and branches being conditional expressions. It's all recursive too; if a function ends on a branch that ends on a loop, the function will return the array from the loop.
It's part of the zen of CoffeeScript to always evaluate to the last thing evaluated.
Of course, we all establish idioms to improve our code and naturally assume it would be nice if they were baked in, but removing implicit returns from CoffeeScript is probably too uncharacteristic to make an argument based on practicality, unless the benefits are really substantial. Being able to omit a few return statements isn't much of a win.
Didn't mean to be rude to anyone. I would like things slightly different myself, but it seems like a bridge too far. I can't see us actually developing a version of CoffeeScript without implicit returns anytime soon. Perhaps some clever syntax to explicitly change the behaviour would fly, but nothing proposed in previous discussions was very well received.
Well said, @carlsmith. I agree.
Thanks for the terrific input all.
I'm closing this for now as any serious modifications to the language unrelated to ES6 are looking out of scope at this point.
Yeah. At this point changing core, widely-used parts of CoffeeScript I think is probably off the table. Too many old stackoverflow posts would be no longer valid, and people would have no idea why. For better or worse, CoffeeScript is a victim of its own success.
It's probably worth closing several other, similar threads at this point. This project started with a broad possible range of focus areas and now that we've narrowed it down it probably makes sense to tie off several conversations.
@GeoffreyBooth, would you like me to take that on?
@rattrayalex Sure. I think weโve reached consensus on preserving as much backwards compatibility as possible, so freewheeling discussions of major syntax changes or potential new languages should probably happen elsewhere.
Issue migrated to jashkenas/coffeescript#4911
What features or aspects of CoffeeScript have caused you the most pain?
What do newcomers to the language find the most challenging?
What are examples of CoffeeScript code that can be particularly confusing?
What features have you almost never seen used that could probably be removed?
Let's use this thread for brainstorming ideas, and try to split off discussions into other threads. Reactions ( ๐ , ๐ ) encouraged โ ๐ means "I agree this is a problem" and ๐ means "I disagree โย this is a good feature and should be kept"