coffeescript6 / discuss

A place to discuss the future of CoffeeScript
162 stars 4 forks source link

CS2 Discussion: Question: CoffeeScript Gripes? #13

Closed rattrayalex closed 8 years ago

rattrayalex commented 8 years ago

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"

realJoshByrnes commented 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?

ohaz commented 8 years ago

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

realJoshByrnes commented 8 years ago

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.

JimPanic commented 8 years ago

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.

JimPanic commented 8 years ago

Additionally, you might want to use a reduce/fold function instead where you use the accumulator as arbitrary counting variable.

carlsmith commented 8 years ago

Nonlocal Assignment

As discussed at length in #1, CoffeeScript uses nonlocal assignments by default, which is unusual and confusing.

Remove the Arguments Type

Make it a normal array (if it's actually referenced).

Array Comprehensions

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

rattrayalex commented 8 years ago

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

carlsmith commented 8 years ago

@rattrayalex - Done. No worries.

carlsmith commented 8 years ago

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.

objectkit commented 8 years ago

There are a few gripes I'd like to throw in here myself, but I'll pare it down to just one here...

Implicit Return Must Die

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

carlsmith commented 8 years ago

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.

MemoryChips commented 8 years ago

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.

carlsmith commented 8 years ago

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.

carlmathisen commented 8 years ago

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.

carlsmith commented 8 years ago

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

carlsmith commented 8 years ago

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.

carlmathisen commented 8 years ago

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.

rattrayalex commented 8 years ago

@carlsmith

This has been discussed before

Can you provide a link?

rattrayalex commented 8 years ago

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?

carlsmith commented 8 years ago

Can you provide a link?

This has been discussed a bunch of times. Here's some examples; there are more:

rattrayalex commented 8 years ago

Wow, some really terrific discussion there. Thanks for digging those up @carlsmith !

carlsmith commented 8 years ago

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.

carlsmith commented 8 years ago

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.

rattrayalex commented 8 years ago

Great idea! https://github.com/coffeescript6/discuss/wiki let me know if you have any challenges

carlsmith commented 8 years ago

Nice one @rattrayalex. Cheers. I just added the page to the wiki.

objectkit commented 8 years ago

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

dadleyy commented 8 years ago

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.

mrmowgli commented 8 years ago

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.

carlmathisen commented 8 years ago

@mrmowgli: You could always do a one liner like this.

result = if condition then true else false
JimPanic commented 8 years ago

Or just

result = condition is true
kirly-af commented 8 years ago

Or even

result = condition
DomVinyard commented 8 years ago

Or even

condition

;)

GeoffreyBooth commented 8 years ago

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โ€.

rashfael commented 8 years ago

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:

carlsmith commented 8 years ago

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.

carlsmith commented 8 years ago

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.

lydell commented 8 years ago

Well said, @carlsmith. I agree.

rattrayalex commented 8 years ago

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.

GeoffreyBooth commented 8 years ago

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.

rattrayalex commented 8 years ago

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?

GeoffreyBooth commented 8 years ago

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

coffeescriptbot commented 6 years ago

Issue migrated to jashkenas/coffeescript#4911