jashkenas / coffeescript

Unfancy JavaScript
https://coffeescript.org/
MIT License
16.49k 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 12, 2016 0:24

Whether we have let and var or not is a different question than whether we have const. The first two are related to scope, and const is about reassignment.

We should keep this discussion about let and var, and move const to a new issue.

Should we even keep CoffeeScript's nonlocal assignment rule? If were making breaking changes, we should consider changing that as it was quite controversial - all assignments are currently nonlocal. The name lookup starts locally, and works outwards until it finds the name and reassigns it. If it doesn't find the name, the assignment is local.

In Python, all assignments are just local, which is what you normally want to happen. Lexical scope exists to keep variables locally defined. You never have to worry about clobbering something in the global scope with a local variable like you do in JS and CoffeeScript.

The problem with Python is that it uses nonlocal statements when you want to reassign names in outer scopes, the way CoffeeScript does by default, and we really want to avoid var and let and any other assignment keywords. They're especially awkward in a language where everything is an expression.

Bikeshedding

I think all assignments should be local, and we should have a variable available inside each function, just like arguments, called outer, that's a reference to the function's outer scope.

You would use outer the same way we use window in the browser to do global assignments:

outer.x = 1

If you had functions nested inside of each other, each lexical scope would have its own outer, so it would naturally be recursive. You could do outer.outer.outer... all the way to the global scope.

Assignments should have always been local, and it's natural to extend the idea behind window to all scopes. Then you don't need assignment keywords, can access any scope, and it works naturally as an expression.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 12, 2016 4:55

const is now often the default in ES6 code. Making it convenient to use would be a plus. But not necessarily a priority.

My hunch is the outer proposal, while interesting, would be too far a departure to take on right away.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 12, 2016 4:58

I don't think there's much reason to use let since coffeescript's var's have (most of) the same benefits; they are block-scoped.

If adding a const keyword isn't too hard, it might be worthwhile.

An alternative would be to have all variables by const by default, and allow explicit var or let declarations. I assume this would be more work, as the scoping code coffeescript undertakes would be mostly-removed.

coffeescriptbot commented 6 years ago

From @DomVinyard on July 12, 2016 8:24

You could do outer.outer.outer... all the way to the global scope.

That sounds like an absolute nightmare. Hard-coding your depth in the scope.

It will be very easy to reimagine all of coffeescript's features under the mandate of 'allowing breaking changes' - but I think there is a good case for picking a few bits of low hanging fruit and then addressing the rest once we're up and running. let/var/const would be a good candidate for leaving alone, i think.

coffeescriptbot commented 6 years ago

From @carlsmith on July 15, 2016 16:11

I don't see a problem with absolute scope references, when scope is already lexical. Still, we have more worthwhile stuff to discuss, so yeah, forget it.

coffeescriptbot commented 6 years ago

From @JimPanic on July 19, 2016 9:17

I really want to see const in this new endeavour in one way or another. Maybe not in the first iteration, but definitely when targeting ES6. This is a key feature for me (and I guess a lot of other people as well) in ES6.

Regarding scoping, I'm in favour of leaving lexical scope as-is as well. In the future it might be a good idea to make let the default, const easy to use and var the least common thing syntactically.

coffeescriptbot commented 6 years ago

From @carlsmith on July 19, 2016 13:20

Hi @JimPanic, welcome.

Regarding scoping, I'm in favour of leaving lexical scope as-is as well.

You misunderstood the problem I was trying to address. CoffeeScript will always have lexical scope as-is. No one wants to change that. It's the way CoffeeScript mandates that all assignments are nonlocal that's the issue. It's always been a pretty controversial feature, and one of the most popular criticisms of CoffeeScript. For an example of the feature, here's two seemingly similar blocks of code, the first is CoffeeScript, the second is Python. They do different things though:

    x = false

    do ->
        x = true
        console.log x # true

    console.log x     # true 

In CoffeeScript, the outer x is clobbered by the inner assignment. Whereas, without an explicit nonlocal x statement, Python will never clobber names outside the local namespace.

    x = False

    def f():
        x = True
        print x       # True
    f()

    print x           # False

We can avoid clobbering x in CoffeeScript too, if we introduce the name into the local namespace as a parameter, but you still have to know that that name exists in some outer scope to preempt the clobbering.

    x = false

    do (x) ->
        x = true
        console.log x # true

    console.log x     # false 

Lacking any real evidence, I can just say from experience with Python that nonlocal assignment is pretty rare. You almost always want assignments to be local. I assume @jashkenas only went with nonlocal assignment to avoid having to have declarators. There's no other sane reason to do it.

If we're going to change the language design to introduce declarators, then we should definitely make assignment local by default and have a declarator for nonlocal assignment. We don't need Python's global declarator as we have window.x already.

If let makes a variable block-scoped and const makes the name un-reassignable and block-scoped, neither does what we need.

Even if we keep assignments nonlocal, if we introduce declarators, we would really need declarators for local, const and local const.

The benefit of having block-scope in CoffeeScript is slim. We can always create a new scope with do ->, and any variables in the function will be effectively block-scoped (again, assuming we don't clobber anything). Having let would allow us to do this:

if foo
    let x = true
    let y = false

But, we can already do this anyway:

if foo then do ->
    x = true
    y = false

JavaScript became considerably more complicated by introducing let, and it was only introduced to fix different problems to the ones CoffeeScript has. We don't have much use for it.

I personally think outer.xis better than nonlocal x because it's explicit about which scope it references, and follows logically from window.x. Still, I'm happy to hear other ideas.

Taking declarators from JavaScript requires changing them to fit CoffeeScript's nonlocal assignment feature, which may in turn require any declarators to be renamed for their new semantics, so we'd basically need to define our own declarators from scratch. And we really should try and do everything we can to avoid introducing declarators, given that they have been rejected from CoffeeScript for seven years, and they make everything look like Java.

jQuery ->

    local const square = (x) -> x * x
coffeescriptbot commented 6 years ago

From @JimPanic on July 19, 2016 13:58

@carlsmith You are right, I did misunderstand.

I always thought CS was favouring the innermost scope in assignments. Lucky me it didn't bite me so far.

coffeescriptbot commented 6 years ago

From @carlsmith on July 19, 2016 14:3

@JimPanic :) It's not obvious what CoffeeScript is doing, especially when it seems to be doing what you'd expect, coming from other languages. It doesn't bite that often, but when it does, it can be totally baffling.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 20, 2016 20:25

Thanks very much @carlsmith for the terrific explanation there. Didn't understand that bit myself so well till now 😄

I do agree that it would likely be too ambitious to change how scoping works, unless we can simplify the coffeescript compiler by staying closer to JS's behavior.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 20, 2016 20:27

[const] is a key feature for me

@JimPanic can you elaborate? I intuitively feel the same way – I enforce const-by-default in my eslint – but curious to hear if there are additional reasons why const is useful.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 20, 2016 20:39

Any thoughts on const being used like so:

x := 7  # const x = 7

And all other = being treated as they are today, either using var or let?

coffeescriptbot commented 6 years ago

From @rattrayalex on July 20, 2016 20:43

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.

@DomVinyard re-reading this, I'm not sure I understand what you originally meant. Can you clarify?

coffeescriptbot commented 6 years ago

From @carlsmith on July 20, 2016 21:29

Adding a constant operator is easier than adding declarators. It changes the language less. The operator is ok, but will get a bit messy when its x :=? 7. If it was a short word, but still a normal operator, it wouldn't look as cryptic: x be 7 and x be? 7 looks more like regular CoffeeScript. I don't think it should be called be though. Just, if there was a good, short word, that would be better than having a :=? operator :)

coffeescriptbot commented 6 years ago

From @JimPanic on July 22, 2016 8:38

@rattrayalex It helps avoiding so many bugs and enables a more declarative way of coding, which in turn helps keep code readable and (more) testable. How do you enforce const by default? Does your linter settings just yell at you when you do not declare something as const? :D

coffeescriptbot commented 6 years ago

From @JimPanic on July 22, 2016 8:39

@carlsmith I was thinking the same; a different assignment operator would go well with the CS syntax as it is now, since there are no declarator keywords. Why the question mark in :=? ?

coffeescriptbot commented 6 years ago

From @carlsmith on July 22, 2016 12:55

@JimPanic - My bad: I got it back-to-front (I rarely use the feature this way), but you have an existential assignment operator in CoffeeScript that looks like ?=, so if we have a constant assignment operator like :=, we'd need an existential constant assignment operator, which would end up looking like ?:=.

coffeescriptbot commented 6 years ago

From @carlsmith on July 22, 2016 13:26

We could figure out from static analysis whether a name is assigned more than once, and make names that are never reassigned constants automatically, but expect JavaScript engines already do this, so any efficiency gains would likely be negligible. More importantly, people want explicit constant assignment/declarations to communicate that constraint to other programmers. Given that it's a popular CoffeeScript feature request, and already part of ES6, explicit constants should really be supported somehow.

There's an argument for getting rid of the is and isnt operators from CoffeeScript. CoffeeScript used to have an aint operator that compiled to !==, but it got dropped in favour of just having the current isnt alias, but that's confusing. People often shorten a is not b to a isnt b, expecting them to mean the same thing, like they do in English, but a is not b compiles to a == !b, while a isnt b compiles to a !== b.

If we just kept the current == and != CoffeeScript operators (which compile to === and !==), and got rid of isnt altogether, then is would be a good constant assignment operator.

    i = 0
    pi is 3.141

Then a ?is b would work as an existential assignment operator.

If we do a backwards incompatible CoffeeScript, we should definitely fix anything that is accepted as broken and easy to fix. Removing is and isnt would be trivial. We already have conventional, unproblematic == and != operators. And is would be a really nice constant assignment operator.

It's also worth mentioning that Python has an is operator. It's used to test whether two expressions evaluate to the exact same object in memory.

    a = b = []
    a is b         # True

    a, b = [], []
    a is b         # False

Using is for assigning constants would be a bit different, but still similar to Python - they both kind of mean is actually rather than equals.

coffeescriptbot commented 6 years ago

From @DomVinyard on July 22, 2016 14:25

@rattrayalex

Coffeescript succeeded, at least partially, because it traded out efficiency optimisations for simplicity in a bunch of contentious but elegant ways. Not distinguishing between let and const fits that core simplicity brief.

Perhaps this means that a few edge-projects will have to use vanillaJS for certain classes of application because the overhead of trading out lets for consts would trip the project from acceptable into unacceptable but I think that is a reasonable cost to keep things simple.

Avoiding having to add a new operator is worth fighting hard for in every case.. if CS is to retain its CSiness.

coffeescriptbot commented 6 years ago

From @DomVinyard on July 22, 2016 14:40

@carlsmith Firm -1 from me for redefining is.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 22, 2016 16:24

How do you enforce const by default? Does your linter settings just yell at you when you do not declare something as const?

Yes – it's an ESLint setting to always use const unless you redefine the variable later.

we'd need an existential constant assignment operator, which would end up looking like ?:=.

I don't think an existential constant assignment operator makes sense 😉. ?= is used for reassignment, which isn't possible with const.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 22, 2016 16:55

explicit constants should really be supported somehow.

I'm increasingly convinced of this as well personally. Should probably add to https://github.com/coffeescript6/discuss/issues/8.

Avoiding having to add a new operator is worth fighting hard for in every case.. if CS is to retain its CSiness.

I disagree with this sentiment, actually. While maintaining a decent degree of backwards-compatibility would be nice, I don't think we should shy away from adding new features, especially those that are part of ES6. The number one purpose of this org is to ensure that CoffeeScript isn't behind ES6 at all – that anything* you can do conveniently in JavaScript, you can do at least as conveniently in CoffeeScript.

Furthermore, language innovation is a core aspect of the "CSiness" of CoffeeScript IMO. Jeremy combined the most convenient language features he could find from other languages, made it easy to write good javascript, and added some new ideas into the mix as well.

We should certainly think really hard about whether any new features are worth doing – and focus on compatibility with ES6 first – but I also don't think it's something we should shy away from.

* Anything that you should be doing, anyway. Making "the bad parts" and generally poor programming practices less convenient, or even impossible, is a good thing.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 22, 2016 16:59

Firm -1 from me for redefining is.

I personally agree. However, to keep a healthy environment where people feel comfortable voicing new ideas they might not be 100% comfortable with yet, let's try to back up -1's with a few calm reasons.

In this case, my reason is primarily difficult of porting – redefining a language keyword to something completely different could make updating quite difficult, as you'd have to both translate all existing =s to is, and only once that is 100%-for-sure-done across your whole project, change most of your =s to is.

I also happen to think it'd be a bit less readable than something with symbols, given the ubiquity of the = for assignment across programming languages.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 22, 2016 17:1

My understanding is the primary objection to := is the conditional assignment scenario, which as I outlined above probably doesn't apply. Are there others?

I think the leading alternative at this point would probably be to just pass const through, or come up with a similar (ideally shorter) word.

Thoughts?

coffeescriptbot commented 6 years ago

From @carlsmith on July 22, 2016 22:0

I don't think an existential constant assignment operator makes sense 😉. ?= is used for reassignment, which isn't possible with const.

I didn't think about that. Sorry about the noise then. I mentioned not using that feature much when I misspelled the operator :|

I'm with @rattrayalex on adding new features, but feel pretty strongly that we should stick to things that are just sugar for well established idioms or that have already been standardised by ECMA. CoffeeScript should make one big breaking change, then treat TC39 as our standards process. Now that CoffeeScript has inspired JavaScript to evolve, we can co-evolve with JavaScript in a way Jeremy couldn't in 2009.

So, yeah - sorry to ramble - assuming there will be no need to have an existential constant assignment operator, then I would personally be ok with :=, but still wish it was prettier.

square := (x) -> x * x

I still feel strongly that we should avoid declarators if at all possible. It changes the language, and we use assignment expressions in lots of places where they need to be concise, not just in standalone statements..

Also, do we need a story for constant function parameters, or can you just not define a parameter as a constant?

coffeescriptbot commented 6 years ago

From @rattrayalex on July 23, 2016 2:11

but still wish it was prettier.

Agreed.

remember that we use assignment expressions in lots of places where they need to be concise.

Would you be willing to provide a few quick examples for illustrative purposes?

or can you just not define a parameter as a constant?

Well, you can't in es6, so we'd really be going above and beyond if we implemented it in cs6. It's not an unappealing feature imo but probably not something to worry about yet.

CoffeeScript should make one big breaking change

Amen.

then treat TC39 as our standards process

I really need to learn more about TC39 😃

coffeescriptbot commented 6 years ago

From @carlsmith on July 23, 2016 2:57

Would you be willing to provide a few quick examples for illustrative purposes?

I meant using assignment expressions like this:

if (stuff = getStuffOrReturnNull) then use stuff

employees = [
    ali = new Employee "Ali"
    bob = new Employee "Bob"
]

Because assignments are expressions, you can write one anywhere you can write an expression. It can be helpful when you want to make assignments conditionally, and lets us use some shorthand expressions for initialising variables.

coffeescriptbot commented 6 years ago

From @carlsmith on July 23, 2016 3:10

On the TC39 comment: I really just meant that we should follow the ECMAScript standards when introducing features that go beyond sugar for common JS idioms.

We have the luxury these days of a standards process that moves quickly, releases often and develop editions in parallel. It has some of the best minds in our community, and all the vendors are on board. You can get involved through ESDiscuss, and influence the spec, but for the most part, we don't need to. It works.

If Jeremy had followed the spec, we wouldn't have the problem we now have with incompatible classes. Obviously, in 2009, following the spec would have made CoffeeScript suck.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 23, 2016 3:13

Wow, I've never seen the employees code before. Looks awesome though.

https://github.com/michaelficarra/CoffeeScriptRedux/wiki/Intentional-Deviations-From-jashkenas-coffee-script#intentional-deviations might be relevant here once we get into the weeds, especially if we decide to build on CSR.

Thanks for sharing the examples! Helpful.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 23, 2016 3:14

Re; TC39, thanks for elaborating - helpful. To clarify, do you propose:

  1. Implement all things agreed by TC39
  2. Set up a similar organization with similar bylaws, and use that to determine the course of the language.

(I'm mostly 👍 to both)

coffeescriptbot commented 6 years ago

From @rattrayalex on July 23, 2016 3:55

Some thoughts on an alternative route to :=.

There are three things that we typically do with =:

  1. Declaration (const myVar, let myVar, var myVar)
  2. Assignment (let myVar = 3)
  3. Reassignment (let myVar = 3; myVar = 'foo')

Reassignment occurs the least often, and is arguably an anti-pattern (though often a practical necessity). const prevents reassignment, let and var do not. const requires assignment with declaration, let and var can be declared without assignment.

There are a couple routes we could go down:

  1. Use const by default, and infer when let/var should be used instead. Leverage coffeescript's existing Scope parser to check if a variable is ever reassigned; if not, it's const. If it's reassigned, it's var or let (we can decide later). Pros: This would be backwards-compatible (modulo some probable bugs) and would result in frequent use of const. Cons: Since there is nothing visible to the coffeescript programmer, the benefits of using const are essentially eliminated. The work of discouraging reassignment would probably have to fall to a linter.
  2. Use const by default, and enforce declarators for let/var. This would make using let/var less convenient; the example above would probably look like this:

    let employees, ali, bob
    employees = [
       ali = new Employee "Ali"
       bob = new Employee "Bob"
    ]
    # later, reassign to different values:
    ali = 5
    bob = 'foo'
    employees = 'other nonsense'

    (the let would be unneeded if the variables were never reassigned) Pros: This would discourage use of reassignable variables, while still allowing it without too much hassle. It should be reasonably tractable to write an automated cs->cs6 transpiler to add the let's, but probably a pain. It also allows use of both let and var without taking a stance. Cons: Declarations are still ugly. Might be too prescriptive.

  3. Use const by default, and enforce special syntax for reassignment (eg; ^=, the update-operator). This is pretty outlandish – I don't know of any other language that has a different syntax for assignment vs reassignment – but I think it's pretty interesting. Imagine reading a program and immediately knowing whether foo = 'bar' is writing a variable or overwriting a variable. It could look like this:

    foo = 1
    foo ^= 2

    Or, perhaps:

    foo := 1  # becomes `let foo = 1`
    foo ^= 2 # becomes `foo = 2`
    bar = 3   # becomes `const bar = 3`

    Or this could be done in tandem with option 2:

    let foo = 1  # becomes `let foo = 1`
    foo ^= 2 # becomes `foo = 2`
    bar = 3   # becomes `const bar = 3`

    Pros: Would strongly discourage, but allow, reassignment. Would be extremely explicit about what is assignment, what is reassignable-assignment, and what is reassignment. Possibly a genuine language innovation. Can get rid of CoffeeScript understanding of scope entirely. Cons: Inconvenient, ugly, foreign.

  4. As mentioned above, default to var and allow const declarations through :=. Pros: Much more backwards-compatible, still allows const for those who really want it. Cons: Discourages the thing you should really be doing; the right thing and the good thing aren't the same.
  5. Default to const, and allow var or let declarations (but only one of the two) through :=. Pros: Relatively simple, avoids declarators, could possibly get rid of CoffeeScript understanding of scope, makes the right thing the easy thing. Cons: Still a little ugly, not backwards-compatible, have to make a choice between let and var.

Thoughts? Any other ideas?

coffeescriptbot commented 6 years ago

From @ozjd on July 24, 2016 17:39

To keep things very CoffeeScript like, I would use the following: (in this order) const: When a variable is named UPPERCASE eg VAR = ... (It's very important to note that this seems to be the common suggestion for coffeescript, and is how most people already write an implied constant) var: When an object is redefined (eg, contains a . as in window.var = ... or [] as in window['var'] = ... let: All other times.

Examples: @foo = 'bar' would become let this.foo = 'bar' @FOO = 'bar' would become const this.foo = 'bar' obj::foo = 'bar' would become let obj.prototype.foo = 'bar' obj::FOO = 'bar' would become const obj.prototype.foo = 'bar' obj.prototype.foo = 'bar' would become var obj.prototype.foo = bar obj.prototype.FOO = 'bar' would become const obj.prototype.FOO = 'bar'

I'm sure there are other cases that I haven't thought of, but I'd imagine that this would also probably break very few existing scripts. Thoughts?

Note: I think the most important part is automatically mapping UPPERCASE variable names to a const.

coffeescriptbot commented 6 years ago

From @carlsmith on July 24, 2016 18:35

I didn't quite understand the code above, but just to be clear: Deleting ali and bob in this code would not change what the array named employees evaluated to:

employees = [
    ali = new Employee "Ali"
    bob = new Employee "Bob"
]

The code creates three names:

Deleting ali would decrement the reference count on <Employee0>, but it would not be garbage yet, as it's still referenced by employees[0].

coffeescriptbot commented 6 years ago

From @carlsmith on July 24, 2016 18:37

I already commented about this on the main CoffeeScript repo, but beings as it's being addressed here as well...

-1 on forcing constants to be uppercase. Most functions will be constants, and we don't want every function to have a LOUD_NAME. We also wouldn't be able to distinguish between class names and the names of regular functions if they all have to be uppercase. We also use camelCase a lot in CoffeeScript, and would be forced to use underscores if we only have uppercase constant names.

A constant assignment operator has been discussed elsewhere, so you'd do something like:

i = 0
pi := 3.141

A better looking operator would be nice, but it's a much less radical change at least.

coffeescriptbot commented 6 years ago

From @ozjd on July 24, 2016 19:52

Note that I wasn't suggesting that you MUST use uppercase for constants, just that UPPERCASE variables would be considered constants.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 24, 2016 17:31

@ozjd I might recommend you familiarize yourself with ES6 a bit.

Variable declaration isn't done as a part of property assignment; let obj.prop results in Uncaught SyntaxError: Unexpected token ..

const is commonly used outside of traditional "constants". For example, AirBnB's style guide (the de facto community standard) recommends using const for as many variable declarations as possible.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 24, 2016 17:40

@carlsmith sorry for my confusing code sample, I didn't mean to delete the variables, just reassign them. I've edited my comment for (arguable) clarity. Does that help at all?

coffeescriptbot commented 6 years ago

From @rattrayalex on July 24, 2016 17:56

Ah, one idea I meant to add to this thread that could make the parser simpler and the language more explicit:

Outlaw implicit window.

Now that ES6 has modules, the value of the convenience of the implicit window has gone way down. It's almost exclusively an antipattern. In some files where I need to use a global, I put this at the top:

const { app } = window; // fake import

... and otherwise just use window.someVariable.

CoffeeScript already does not allow implicit writes to the global object, but does allow implicit reads:

thing() ->
  f()  // would essentially call `window.f()`

Should we do this if it makes life easier?

coffeescriptbot commented 6 years ago

From @GeoffreyBooth on July 24, 2016 21:05

I guess an early question to answer is whether we want to give the developer the power to specify when let or const is output. In other words, what's the goal here:

  1. To build the ability for the compiler to have the smarts to know when to use let, when to use const, and when to use var? And current syntax is unchanged.
  2. To allow the developer to force a const for a particular variable? And the compiler would automatically use let or var for all others.
  3. To allow the developer to force a let for a particular variable? And the compiler would automatically use const or var for all others.
  4. To allow the developer to force either a const or a let for a particular variable? And the compiler would automatically choose the best option for all others.

I assume we at least want to do the first goal, and that should get its own issue to figure out the methods for how the compiler will decide what to do. If we want to do one of the others, perhaps a new thread can be created where we can hash out the desired functionality and new syntax.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 25, 2016 20:28

Terrific summary @GeoffreyBooth . Personally I'm interested in 1, 2, or 3. I'd prefer 3 but for backwards-compatibility would accept 2.

const exists, and is used, for a reason. That reason goes away if the compiler infers which one you mean, hence my unease with option 1. What's the point of const at that point?

coffeescriptbot commented 6 years ago

From @carlsmith on July 25, 2016 20:31

The point in that case would be that the JS engine gets to do all the optimisation, without us having to tell it what to do manually. We get all the benefits, with none of the labour.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 26, 2016 22:05

What benefits?

coffeescriptbot commented 6 years ago

From @carlsmith on July 26, 2016 5:12

Perf.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 26, 2016 10:48

Sorry, I should have been more clear. I don't think performance is the reason people use const in JavaScript.

As of Oct 2015:

Using const is consistently slower in all tests but it is marginal and too close to call.

And was much slower when it was first released. It might be faster in the long term, of course, but for now it's not – especially if you use Babel, and the const is turned into a var anyway. As such, it probably wouldn't make sense to enable const at all if performance is the argument, until there is indeed a dramatic performance boost from using it. At which time, yes, implicit const would probably be fine.

coffeescriptbot commented 6 years ago

From @rattrayalex on July 26, 2016 10:48

My impression is that const is used for preventing the class of bugs related to reassigning variables. Eg; from the AirBnB style guide note on the topic:

This ensures that you can't reassign your references, which can lead to bugs and difficult to comprehend code.

From the ESLint docs:

If a variable is never reassigned, using the const declaration is better.

const declaration tells readers, “this variable is never reassigned,” reducing cognitive load and improving maintainability.

These benefits don't apply if const declaration is invisible and goes away the moment you reassign a variable.

coffeescriptbot commented 6 years ago

From @carlsmith on July 26, 2016 11:03

Yeah, that's the controversial bit. Jeremy could have added constants to CoffeeScript at any point - the compiler knows which vars are reassigned - but didn't. The perf thing is a definite benefit if it works, but as you pointed out, it doesn't seem to. The benefit of explicit constants has to be weighed against complicating the language.

Personally, I think you're right: It's a feature people want, and it's now standardised, while it wasn't when Jeremy decided against it, and the complication is pretty minor, especially for the kind of people CoffeeScript appeals to, so I'd vote to accept a PR if asked.

If we work on the basis that concensus means that nobody strongly objects (as it's impossible to get everyone to agree), then the question is really simple...

Does anyone feel strongly that we should not add explicit constants?

coffeescriptbot commented 6 years ago

From @GeoffreyBooth on July 26, 2016 11:39

Personally I think we should do both: the compiler should automatically choose the best of var/let/const for each variable assignment, and we provide developers a way to force a const for a particular assignment. I don’t see a need to provide a way to force var or let.

I like the idea of echoing the @foo as our way of designating a constant, by using some symbol other than @ to designate a constant. My instinct would be to use ! but that already means “not.” So . . . ^foo? %foo? Is there something standard from other languages?

coffeescriptbot commented 6 years ago

From @carlmathisen on July 26, 2016 11:47

Agree with @GeoffreyBooth that we don't need to worry about var or let when writing CS code. I mean, CS takes care of that scope for you under the hood anyway.

Regarding const assignment: In normal JS and other languages, you just reference a constant like a normal variable, don't see why CS needs to do this differently. IMHO, it just adds unnecessary complexity to syntax.

Why not just const foo = 'bar'?

coffeescriptbot commented 6 years ago

From @JimPanic on July 26, 2016 11:52

I feel strongly in favour of adding some form of const syntax. It does not have to be a declarator keyword. To keep the concise nature of CS, the aforementioned := for const is what I would like to see.

Regarding let and var: since const will then not be the default, let should be the default behaviour. var could be explicitely declared using the var keyword.

@carlmathisen: I think the main thing with this is, there are no declaration keywords in CS and it would change the language quite a bit.

coffeescriptbot commented 6 years ago

From @carlsmith on July 26, 2016 15:25

If we're compiling to ES6, we'll definitely end up using let in compiled JS (whether we redo CoffeeScript's nonlocal assignment or make assignments local or whatever happens). CoffeeScript currently wraps stuff in self-invoking lambdas to create the same effect withvar, so we'd definitely use let instead these days.

Everyone who's contributed so far seems to agree we should have explicit constants, that compile to const and so have ES6 semantics (the name can't be reassigned, but the value can still be mutable). We keep going back to declarators though. I'm pretty strongly against them. They are too bulky:

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

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

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

CoffeeScript hasn't got declarators, so adding them is a big change. From that example (which I only went with because I use it all the time), const is used five times. It makes much more sense to use an operator. Adding an operator doesn't change things as radically, though it does change things substantially still.

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

I put the equals first because GitHub kept trying to insert people's names when it's written the other way around.

There's the option of appending something to the beginning or end of a constant, but that seems pretty bulky in practice too.

@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

If it looks like a different programming language, it's gone too far, so only an operator seems acceptable. If we could get it down to one character, then it would be better still, but looking at the keyboard, there's nothing left, so something like =: seems like the best compromise, but it is a bit ugly still.

We could use unicode, but even in a language for the Web, that's still a big change. Looks good though.

i = 0
pi ▹ 3.141

It would be nice to support unicode for people who want to use it, in a subproject (APL Mode). Using stuff like , , and would make code less cryptic and prettier, and isn't hard to enter once you start using Alt and Alt+Shift for a little while.

Given that so many CoffeeScript users like to do language design, we should give plenty of thought to letting people do stuff like configure the compiler, expand macros and walk the AST mutating nodes. Obviously, we can't do everything, but letting people have the dialect they want would be cool.

Sorry for such a long post.