jashkenas / coffeescript

Unfancy JavaScript
https://coffeescript.org/
MIT License
16.52k stars 1.99k forks source link

CS2 Discussion: Features: let/const/var #4900

Closed coffeescriptbot closed 6 years ago

coffeescriptbot commented 6 years ago

From @DomVinyard on July 11, 2016 12:13

Personally, I believe these should be untouched. I can see that there is a case for implementing const in almost any language remit. But not coffeescript.

Copied from original issue: coffeescript6/discuss#1

coffeescriptbot commented 6 years ago

From @carlsmith on July 26, 2016 15:34

Just really wanted to add something: There's a long list of abandoned CoffeeScript dialects. If we could find a way to allow people in future to implement a dialect through the official API hooks and compiler configurability, then we could bring all that stuff into one project with a bunch of third-party language extensions and entire domain specific dialects available.

I've been playing with Pratt parsers quite a bit, and they are relatively easy to provide as a library the user can easily extend. It's just a bit awkward without module support, because there's some intrinsically shared state, but I reckon it could be done that way really well. The issue with CoffeeScript for any parser is all the implicit stuff.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 26, 2016 17:19

@carlsmith your example should read:

factory := (mutator) -> (args...) ->
    mutator (self := Object.create null), args...
    return self

Employee := factory (self, name, salary) ->
    self.name = name
    self.salary = salary
    self.raise = (amount) -> self.salary += amount

ali := Employee "Ali", 50000
ali.raise 10000

recall that properties (eg; self.name) are not declared.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 26, 2016 17:24

Alright, it's definitely sounding like := as const is the way we'll go for now.

If we can use let instead of var too, cool, but I don't think there's a reason to prioritize that.

Backwards-compatible, simple, concise. Lets people use it who want to, nobody has to. Should simple to implement in relative terms.

Sound good?

coffeescriptbot commented 6 years ago

From @dadleyy on July 26, 2016 18:30

In my ideal version of what we're trying to build here, I'd sooner have the compiler use ALL_CAPS as an indicator of const-ness than adding an operator.

It seems that if the compiler is going to be intuitive about two out of these keywords, using an operator like := makes the third an oddball even though they're all related.

When I write things in all caps (which I do), my intention is to indicate to other people working in my code that that symbol's value is not going to change and I think thats consistent across other languages too (whether or not they have the concept of const-ness).

coffeescriptbot commented 6 years ago

From @rattrayalex on July 26, 2016 21:03

@dadleyy have you programmed in ES6 much? const is used differently than in most other languages.

coffeescriptbot commented 6 years ago

From @JimPanic on July 26, 2016 23:07

@dadleyy const in JS is not about immutability, which would be indicated in other languages by using all-caps variable names. It is about the fact that a variable declared with const can only be assigned to once.

This essay lays out the characteristics of let, var and const in a very comprehensible way: http://raganwald.com/2015/05/30/de-stijl.html

coffeescriptbot commented 6 years ago

From @dadleyy on July 27, 2016 6:10

I understand it does not prevent users from mutating the value stored, e.g:

const user = {name: "johnny"};
user.name = "bill";

But I just think that if someone modifying the contents of a something identified as const is bad practice - whether or not it's allowed. Just personal opinion; the majority of my use for this in es6 code is to define string, number, and regex literals:

const MAX_ATTEMPTS = 3;
const INVALID_BUDGET_ERROR = i18n("transactions.invalid_budget");
const VALID_INPUT_RGX = /[A-Za-z]/

Wherein the value is almost immutable anyways. Is there an example of a situation where the code uses const with an object/array that it does modify that is beneficial to the code? Where are you guys using const today and for what?

coffeescriptbot commented 6 years ago

From @JimPanic on July 27, 2016 6:16

I'm using const actually everywhere and make var or let the exception for when I really need to reassign a variable. I wouldn't want most of my function scope to be cluttered with all-caps variable names. It would look a bit odd, I think. 🤔

coffeescriptbot commented 6 years ago

From @dadleyy on July 27, 2016 6:19

ah. I feel like having it cluttered by := would be worrysome to though, no? Is the best case scenario here a compiler that detects re-assignment and appropriately decides let/const/var without explicit user involvement?

coffeescriptbot commented 6 years ago

From @JimPanic on July 27, 2016 6:32

Since the assignment with := only happens once, but usage of variables is probably more often, I wouldn't think so.

My problem with inference of let/const/var is that it completely goes against the advantage of const as an explicit marker. You'll never know whether a variable is declared as const unless you read the complete code of the current scope.

I do get your point with all-caps const variables and I've used that in Ruby a lot. But it kind of bugged me to write and read all-caps variable names as well.

What do other people think about this?

coffeescriptbot commented 6 years ago

From @JimPanic on July 27, 2016 6:35

It all boils down to taste, and I'm frank about it: I don't like all-caps words and I find := vs. = to look better and coincidently they correlate quite well with the mathematical use of those operators: https://en.wikipedia.org/wiki/List_of_mathematical_symbols#Symbols_based_on_equality .

So basically your proposal would work equally well but I personally don't like it as much. :)

coffeescriptbot commented 6 years ago

From @carlsmith on July 27, 2016 6:44

I'm totally against SQUARE = (x) -> x * x. It's a nightmare to have to hold Shift every time you type a name as well.

It also destroys all of our naming conventions, which span many languages.

coffeescriptbot commented 6 years ago

From @dadleyy on July 27, 2016 6:52

Yeah I didn't realize people were using const so much; having too many uppercased symbols would definitely get annoying (though one or two is definitely fine @carlsmith, I wouldn't consider that a nightmare). I'm just worried about departing from coffeescript too much by adding :=. If that's what people want and it makes it in, I'm still going to use this new language so my case is pretty moot.

coffeescriptbot commented 6 years ago

From @carlsmith on July 27, 2016 7:03

One or two is fine, but most names are constants.

@rattrayalex - In the example I posted, Employee should still be a constant.

Why can we not make properties constant? We can make properties of window constant. Every name is really just the property of some scope. I don't like the idea of adopting broken operators. If ES6 has a broken constant operator, then maybe we need to fix it.

coffeescriptbot commented 6 years ago

From @carlmathisen on July 27, 2016 8:46

@carlsmith: We can make properties of window constant.

Didn't know that - how?

coffeescriptbot commented 6 years ago

From @carlsmith on July 27, 2016 9:21

I just meant that global assignments are to properties of the window object, so outside lexical or modular scope, const x = 1 and const window.x = 1 are equivalent, but the second is apparently illegal.

I've tried to read up on this, as it's a bit confusing - you assume it's as simple as it seems - but the usual docs don't dive into any detail. Gonna search ESDiscuss later, and see what's been discussed already.

coffeescriptbot commented 6 years ago

From @carlmathisen on July 27, 2016 9:44

@carlsmith: const x = 1 and const window.x = 1 are equivalent

I don't think that is the case, because var x = 1 is scoped, and x = 1 (without var, let or const) is assigned to window.

const x = 1;
alert(window.hello);

outputs undefined, while

x = 1;
alert(window.hello);

outputs 1

coffeescriptbot commented 6 years ago

From @carlsmith on July 27, 2016 12:13

Thanks @carlmathisen. Yep, you're right. I never knew it worked like that. It just never came up. That makes things consistent at least. And the ability to make properties read-only makes constproperties redundant, which wraps the whole thing up. Thanks again for putting me straight. I at least understand the decisions now.

ES6 gave JavaScript features we really needed, but also made a pretty messy language even messier :(

We ultimately need a new language, with JS bindings, but with its own semantics that refine JavaScript, removing cruft like the special arguments array type and 'temporal dead zones', and fixing coercion and exception handling etc. etc. Once WASM matures, it'll be much easier to do without the performance penalty of using an interpreter VM written in JavaScript instead of just compiling to JS.

coffeescriptbot commented 6 years ago

From @DomVinyard on July 28, 2016 1:11

We ultimately need a new language, with JS bindings, but with its own semantics that refine JavaScript

I'm here because I need Coffeescript. And because it would be nice to get a community together again to lovingly support it going forward.

It's still my belief that there is no need for a var/let/const distinction in Coffeescript and that 'a variable is a variable' is enormously powerful.

coffeescriptbot commented 6 years ago

From @JimPanic on July 28, 2016 4:09

ES6 gave JavaScript features we really needed, but also made a pretty messy language even messier :(

That's certainly true. I hope a few of those messy bits will get cleaned up over time in the next years, though. Given the pace ES is moving at right now, this is not too unlikely.

It's still my belief that there is no need for a var/let/const distinction in Coffeescript and that 'a variable is a variable' is enormously powerful.

I think it's a good feature to have as long as you don't have to use it. I do want to be able to use const if I deem necessary, though, and not have CS stand in the way.

coffeescriptbot commented 6 years ago

From @carlmathisen on July 28, 2016 4:26

But is it a core focus feature, right now? It's absence is certainly not in the way of ES6 compatability, such as classes.

IMO it's nice to have at best, not need to have.

coffeescriptbot commented 6 years ago

From @JimPanic on July 28, 2016 4:32

const is quite a low-hanging fruit, I think (but am not sure). Personally, for me it is a need a to have, as opposed to classes which I don't use at all. So this is a matter of personal priorities and eventually the priorities we agree upon in the project. It does not seem to be a technical decision after all. :)

It is not listed as the proposed core focus features, but as one of points in the "four core may be too short"-list. Can you maybe head over to the https://github.com/coffeescript6/discuss/issues/8 issue and comment there as well regarding priorities?

coffeescriptbot commented 6 years ago

From @DomVinyard on July 28, 2016 8:03

Personally, for me it is a need a to have

I can fathom the sort of project where the overhead of const over var is a performance bottleneck remedy. I can't fathom why anybody would pick coffeescript for that project.

coffeescriptbot commented 6 years ago

From @carlsmith on July 28, 2016 8:45

We ultimately need a new language, with JS bindings, but with its own semantics that refine JavaScript

I'm here because I need Coffeescript. And because it would be nice to get a community together again to lovingly support it going forward.

I was making an observation about what the world ultimately needs. I don't see how your motivations for being here counter that.

Personally, for me it is a need a to have, as opposed to classes which I don't use at all.

The ability to extend ES6 classes is required to use the APIs provided by modern JavaScript libraries. If you don't have that feature, you can't use a growing list of libraries. We don't need constants for ES6 interop. It's not about personal preferences. We need ES6 classes to avoid becoming obsolete, but constants are entirely optional.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 28, 2016 10:21

const is a nice-to-have in my book as well, but @JimPanic is certainly not alone in wanting it.

Let's talk priorities on the priorities thread, and avoid shooting down ideas just because they're not "absolute must-haves"

coffeescriptbot commented 6 years ago

From @carlmathisen on July 28, 2016 9:29

You're right @rattrayalex, sorry about the tangent.

coffeescriptbot commented 6 years ago

From @GeoffreyBooth on Sep 6, 2016 12:03

Carrying over a discussion from https://github.com/coffeescript6/discuss/pull/30, is interoperability affected if we don’t add support for const? Like will there be any library we can’t use if we can’t send a const-defined variable into it, for example? Or perhaps a build tool that optimizes based on const vs var or let?

One sort-of case I can think of is mentioned in this comment on the modules PR. Basically, anything you import is essentially a const. In this:

import foo from 'lib'

foo is read-only, meaning it’s essentially a const; if you try foo = 2 later on, an error will be thrown. I wonder if it’s weird to have these implicit constants defined via import, while no other constants can be created in any other way. @greghuc, with regards to your ”mental model” of needing to keep track of which variables are read-only and which aren’t, unfortunately with module support you have no choice but to fight that battle (assuming you want to use modules).

coffeescriptbot commented 6 years ago

From @zeekay on Sep 6, 2016 17:23

Any thoughts on const being used like so: x := 7 # const x = 7

I'm not a fan as := for const declaration as it's used in Go (and other languages) for variable declaration. If we were going to break nonlocal assignment in CoffeeScript I'd prefer to use := for new variables and = for assigning to an existing variable (as in Go). This makes assigning to a variable in an outer scope very clear and tends to actually prevent bugs whereas I've never found const to provide much value.

As const does seem to be fairly popular though, support makes sense. I'd argue that we should just we make const pass through like other ES6 constructs.

const x = 7

This is perfectly acceptable and matches JavaScript which is preferable to new syntax. It's readable and fine, in my opinion.

Coming from Python, CoffeeScript's nonlocal assignment was kind of confusing, at least initially. I no longer find it very difficult to reason about, but I cannot think of a single person I've met who is a fan of it (and of course it's been written about for quite some time, never positively). Personally, I would not mind ditching it in favor of implicit let or explicit :=/=.

coffeescriptbot commented 6 years ago

From @GeoffreyBooth on Sep 6, 2016 17:57

zeekay I think := was selected because const can only be assigned as part of its declaration, so it was appropriate. There is a strong convention in CoffeeScript to not use keywords for declaration (hence no var, no function, etc.; exceptions exist like class, but I don’t think people want to add any more). We can’t break backwards compatibility, so changing the behavior of = is not an option.

By nonlocal assignment, I assume you mean CoffeeScript’s habit of creating a var line at the top of whatever scope you’re in and declaring all variables there, then assigning them wherever you reference the variable? That won’t be possible with const: by definition, it needs to be assigned when it’s declared. I assume that support for const would put its declaration/assignment at the same place in the code that you typed it in CoffeeScript, rather than moving it up to the top of the scope.

coffeescriptbot commented 6 years ago

From @zeekay on Sep 6, 2016 18:48

By nonlocal assignment I was referring to how it's not possible to shadow an outer variable:

x = 1
do ->
    x = 2 # x assigns to nonlocal x above
x # == 2

Which is the terminology used above in this discussion (as a nod to Python's nonlocal statement, I believe). The default of nonlocal assignment is very confusing to newcomers and does lead to bugs, in my experience.

I would argue that the lack of keywords for declaration was due to lack of necessity (when CoffeeScript was introduced there was only var to be concerned with). CoffeeScript has actually introduced a great number of keywords (loop, do, is, isnt, etc). I tend to find keywords easier to read and reason about compared to new syntactic structures and that's been one of the things I've come to appreciate about CoffeeScript.

coffeescriptbot commented 6 years ago

From @zeekay on Sep 6, 2016 18:58

As a quick follow-on: I'd be fine with changing declaration behavior. Compiling normal declarations directly to let would lead to an improvement in the readability of the compiled JavaScript and more closely match what modern JavaScript developers would write by hand.

The more interesting bit of the discussion in my mind is what to do (if anything) about CoffeeScript's (for lack of better terminology) nonlocal assignment. It might be nice to introduce := as a way to force declaration, which would not break backwards compatibility:

x = 1
do ->
    x := 2 # declares a new x
x # == 1

Which would be a better use of := than as shorthand for const.

coffeescriptbot commented 6 years ago

From @GeoffreyBooth on Sep 6, 2016 23:52

@zeekay may I invite you to open an issue suggesting changing nonlocal assignment? And the community can debate its merits there.

This thread covers both explicit const assignment (the proposed := operator) and automatic usage of let when appropriate. These are really two separate feature requests, especially considering that the former can be implemented today without a flag (since it’s opt-in by using the new syntax) while the latter can’t (since it’s automatic). I created https://github.com/coffeescript6/discuss/issues/31 to cover the proposed new operator. Automatic lets are much farther in our future, when we’re onto the task of converting all of CoffeeScript’s output to be as ES-latest as possible, so I’m not going to bother creating an issue for it (yet). But feel free if you want to discuss it further.

coffeescriptbot commented 6 years ago

From @mrmowgli on Sep 7, 2016 16:46

I can see why the const becomes important, especially when dealing with code security. Let being the counterpart to avoid intentional abuse of the browser environment. I don't think anything would directly break, but I do think there is a good reason to include the functionality. Perhaps there are some intelligent rules to creating let assignments automatically. +1 for := assuming let and keeping the const keyword.

coffeescriptbot commented 6 years ago

From @rattrayalex on Sep 7, 2016 21:33

@GeoffreyBooth not sure if anyone answered your question:

is interoperability affected if we don’t add support for const?

I don't believe so, no. const is a feature people like, but not an interoperability concern.

coffeescriptbot commented 6 years ago

From @carlsmith on Sep 8, 2016 13:54

I can't see any way it could undermine interoperability either. It's the reference that is constant, not the value, and a library wouldn't even see the name, unless you were sharing globals. Every interface is focussed on the values, never the way they're expressed.

coffeescriptbot commented 6 years ago

From @rattrayalex on Sep 9, 2016 9:10

My understanding of our consensus:

  1. We'll probably just keep compiling to var for the time being, as their is no practical benefit of using let or const in the output.
  2. const may be implemented with := or something similar, but won't be inferred; discussion for that has moved to https://github.com/coffeescript6/discuss/issues/31.
  3. At some point in the future, coffeescript may compile to let/const instead of var, but it's not a priority and not worth discussing now.
  4. The decaffeinate project can be used if you want ES6 output today.

As such, I am closing this issue for now. If I get sufficient đź‘Ž reactions to this comment, we'll re-open.

coffeescriptbot commented 6 years ago

From @GeoffreyBooth on Sep 9, 2016 9:22

If I could amend that consensus a little, @zeekay described an alternate proposal on Gitter that he’s planning to present as a new issue soon, probably this weekend. Basically his point is that the great advantage of let/const is its block-scoping, i.e.:

let a = 1;
if (true) {
  let b = 2;
}
console.log(b); // undefined

This is a dramatic improvement over var, and a big reason why let and const have become popular features. We need to at least discuss if this is something CoffeeScript should have, separate from the feature of const that means “throw an error on reassignment.”

I think we could have both, via := and perhaps :== operators (or whatever two operators people think are best):

We don’t necessarily need the second operator, if we don’t care to give people a way to force const. I’m not sure how popular the “throw an error on reassignment” feature is, but I suspect it’s quite popular.

Agreed that we should close this issue though, in favor of https://github.com/coffeescript6/discuss/issues/31 and whatever new issue is created to discuss this “lexical assignment operator”.