New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why "let" is important #4015

Closed
babakness opened this Issue Jun 18, 2015 · 20 comments

Comments

Projects
None yet
@babakness
Copy link

babakness commented Jun 18, 2015

https://youtu.be/rwm5JLqCpdk?t=17m49s

In CoffeeScript the "let" behavior requires old tricks--a function callback that re-declares the variable to its block each iteration. Simple syntax sugar like -> and => does in addressing scope, would be awesome.

Hopefully not perceived as a duplicate of #3571

@tigerhawkvok

This comment has been minimized.

Copy link

tigerhawkvok commented Jul 27, 2015

Here's the MDN on let

It's getting support with V8 4.5, which will be released with Chrome 45 stable, which, based on the Chrome 44 stable release date, should land 5-8 weeks from now.

Right now, trying to compile with let returns

error: reserved word 'let'

As of 1.9.3

@vendethiel

This comment has been minimized.

Copy link
Collaborator

vendethiel commented Jul 27, 2015

I'll let @jashkenas weight on that one (since coffeescript doesn't have declarators)

@tigerhawkvok

This comment has been minimized.

Copy link

tigerhawkvok commented Jul 27, 2015

@vendethiel I know, I figured that since "let" is an exception rather than rule, anyway, it'd be explicit.

Otherwise, maybe a prefix-symbol? Maybe & or ^?

foo = bar # Standard scope
&baz = bam # Block scope, eg, "let"

I'd almost want -> to work, actually.

baz -> bam

But I'm not sure of a way it could be disambiguated between "I want a function here" and "I want a block-scoped variable here"

@davidbailey00

This comment has been minimized.

Copy link

davidbailey00 commented Oct 4, 2015

Maybe break compatibility and automatically decide when to use let? E.g. var is used by default and let is used inside things like if statements, loops etc.

myVar = "hello"
if myVar is "hello"
  myOtherVar = "goodbye"

would compile to

var myVar = "hello";
if (myVar === "hello") {
  let myOtherVar = "goodbye";
}
@bjmiller

This comment has been minimized.

Copy link

bjmiller commented Oct 4, 2015

I'm sorry. can we please stop with this "let" business, and let it go? ES5 and ES6 force you to think about variable scope every time you write a function. CoffeeScript means that you don't have to. All of the pitfalls you would worry about are automatically avoided.

When let is supported by the top two or three production versions of every JS environment, we can consider whether the existing behavior should leverage it under the hood instead of creating closures, if there is a necessary performance gain. But, that should never affect the syntax, which should stay the way it is.

@davidbailey00

This comment has been minimized.

Copy link

davidbailey00 commented Oct 4, 2015

CoffeeScript means that you don't have to [think about it].

That's pretty much what my comment suggested -- have CoffeeScript decide for you automatically. Of course, as you said, this doesn't need to be the default until most major platforms support it.

@loveencounterflow

This comment has been minimized.

Copy link

loveencounterflow commented Oct 16, 2015

@bjmiller I cannot quite agree given that CS has a weakness that has been pointed out by many, one that it shares, in the genes as it were, with Python, and that is automagic lexical scoping controlled by sameness of names across scopes. For example, when you have

f = ->
  x = 42 # A
  g = ->
    x = 108 # B
    return x
  g()
  return x

then if the variable ('declared' and) assigned at point B happens to be identified by the same sequence of characters as the one at point A, you invariably get a single x declared in the A scope which will then be taken over into the B scope—there's no way to separate the two names.

JS has the weakness that, should you forget to declare x with var or let, you get a global variable; also, there's no formal way to positively indicate at point B that what you want is indeed the x that is shared via nesting of lexical scopes.

CS is way safer and more convenient that JS, BUT it does totally lack a way to have two independent variables by the same name in a surrounding and a nested scope.

This has long be a peeve of mine, in CS as well as in Python. Basically, their rules work well for a lot of cases, but there are cases where you want to distinguish the present scope from the enclosing scopes (actually, the global scope can be distinguished with global). I'm not aware of any language that allows to make that distinction explicit, but we can imagine there to be symbols and / or names that stand in for those.

Just as a strawman, we could say that just as

  • @x and this.x stands in for the 'current dynamic scope',
  • ^x and there.x could be used for the enclosing scope (to the exclusion of the present scope; ^ is like an arrow pointing there), and
  • °x and here.x for the present scope (° looks like a small @; thishere).

Granted that ^ is impossible since it'd clash with an existing operator (which IMHO opinion should always be written with surrounding whitespace) and there is just a single letter apart from here, you could then go and rewrite the above to either the left or the right form:

f = ->                      |   f = ->
  x = 42                    |     x = 42    
  g = ->                    |     g = ->
    ^x = 108                |       °x = 108    
    return ^x               |       return °x
  g()                       |     g()
  return x                  |     return x

The ^x form points explicitly 'away' from the present lexical scope, so wherever you cut'n'paste g into, it will never get a variable assignment of its own and always rely on there being some enclosing scope with x. The °x form explicitly shadows any enclosing scope and will always compile g into a function(){} that starts with var g or let g (as the case may be).

@Fonger

This comment has been minimized.

Copy link

Fonger commented Mar 16, 2016

+1

@samuelhorwitz

This comment has been minimized.

Copy link

samuelhorwitz commented Mar 31, 2016

Two quick things: I think that CoffeeScript should remain doing what it is for 99% of the cases. Don't try to get too clever, just assume all variables go to the top of their enclosing scope as vars.

One exception would be perhaps automatically make for...in and for...of loop declarations use let instead of var unless there is too much potential for breakage.

Finally, completely sidestep the issue of never explicitly declaring a variable without initializing and use a new syntax for let such as :=. You still would only create variables when they are actually needed, but they would be block scoped instead. If you really want to get crazy, let const be ::= but I'm not really in love with that syntax.

@aurium

This comment has been minimized.

Copy link

aurium commented May 14, 2016

I think much like @samuelhorwitz, however i don't like any proposals for defining let. °x or x:= are both unclear ways to say something.

One great thing on Coffee is to make the code more readable, also for who don't know Coffee or even for who is not a programmer. like switch...when...then, if a then b else c (replaceing a?b:c), and the booleans words.

Coffee is not about to write fancy JS, it is about to make it simple and better. So, if we really need let and const, i believe we must use exactly this words in coffee.

I should enforce the @samuelhorwitz's proposal of make for...in and for...of loop declarations use let instead of var!

@carlsmith

This comment has been minimized.

Copy link
Contributor

carlsmith commented May 19, 2016

-1 Being able to do something like local x = 1 might be handy now and again, but parameters are already local:

x = true
f = -> x = false # x is not a parameter
do f
console.log x # puts false

x = true
f = (x) -> x = false # x is a parameter
do f
console.log x # puts true

And, of course, you can just use a different name, even if it's just local_x.

@aurium

This comment has been minimized.

Copy link

aurium commented May 20, 2016

Yeah @carlsmith, you are right, however to propose to use a function, when you want a local variable is not an overwork with no true meaning?

Legibility is about meaning. I only want x, inside my loop, to not mess with x on upper scope. It is much more descriptive to write local x or let x then (x)->. The function demands the reader to understand the meta solution to discover that is a scope protection. If you think "i can use comments" yes... but comments is a proof of poor legibility.

Just local_x is a usable solution on any language. Why some languages allows us to use local x or let x? Sometimes it is simpler and "Keep it Simple" is a good practices. So...If you wont, ok. If someone else wants, why not?

+1 for let!

@connec

This comment has been minimized.

Copy link
Collaborator

connec commented May 21, 2016

I'm -1 for variable declarations. I think let should be the default declaration mode, certainly in loop bodies. Probably, let should replace all occurrences of var in ES6 code as its behaviour is less surprising, similar to the arguments for CS choosing === instead of ==.

I don't think variable shadowing is a good argument for supporting declarations, a lot of linting tools for various languages will complain about shadowing (explicitly with var or with closure arguments) on the grounds that it can be confusing and prevents the shadowed variable from being accessed.

I do find const quite appealing, but its immutability properties are pretty weak in a heavily object-driven language like JS, so I also don't think its enough to justify adding declarations to CS.

@carlsmith

This comment has been minimized.

Copy link
Contributor

carlsmith commented May 21, 2016

It's worth mentioning again that people just have to accept sometimes that CoffeeScript already has pretty much all the features it's going to ever have. There may be some growth around stuff like modules or async, but new features that change the look and feel of the core language are pretty unlikely.

If you concatenate all the new features that different users feel they need to avoid migration to ES6, they are basically asking to overhaul CoffeeScript from the ground up, to make it more modern. I can't see that ever happening wholesale. Someone will have to start again, and design something with modern JavaScript in mind from the start. It's seems more realistic to just expect a new language, with its own ideas, even though any language for the Web will build on ideas from CoffeeScript.

@ibmua

This comment has been minimized.

Copy link

ibmua commented Jun 20, 2016

That is what opensource and GIT forks are for, isn't it? To not completely rewrite stuff when you want to tweak some small features.

Even more, this should be in the main fork and users should be able to compile code in such a way with --let option.

Why "let by default" should be the main feature of the next generation of CoffeScript?
from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

var list = document.getElementById("list");

for (let i = 1; i <= 5; i++) {
  let item = document.createElement("li");
  item.appendChild(document.createTextNode("Item " + i));

  item.onclick = function (ev) {
    console.log("Item " + i + " is clicked.");
  };
  list.appendChild(item);
}

https://jsfiddle.net/6yrcdaxf/

The example above works as intended because the five instances of the (anonymous) inner function refer to five different instances of the variable i. Note that it does not work as intended if you replace let with var, since all of the inner functions would then return the same final value of i: 6. Also, we can keep the scope around the loop cleaner by moving the code that creates the new elements into the scope of each loop.

This is the most logical and expected behavior. Behavior of var is totally unexpected. It only works well if you're not doing any parallelism, or asynchronicity. I came across this problem several times and it is one of the things people hate asynchronous JS coding for. LET behavior, on the other hand is parallelizable and is totally what OpenMP does in such cases when you use it's "#pragma omp parallel for". OpenMP, arguably, is the best parallelization software. Because of such things.

Also, the other example from the above Mozilla JS help is also influential.

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}

Let is definitely a more logical behavior in this case too.

Regarding browser support, first important thing is that you don't care about that when working with NodeJS. Second is that you don't care much if CoffeScript actually compiles to LET, or if it provides some workaround instead. I think there should be --let-native and --let-emulate directives for those.

Just like that, we need a --jsx option for working with ReactJS's https://facebook.github.io/jsx/ , because ReactJS is too good to ignore and too syntactic to code with plain JS.

Edit: actually, looking at ECMAScript6, it seems like CoffeeScript is going to die anyway. Modern JS has many great new features and is much easier to write than it was and it now makes sense to use it (and Babel JS) instead. It would be hard for Coffee to catch up with all of the new features coming out as fast as they do (every year now?) and coffee would become too complex and not very beneficial to learn vs the future JS.

Edit again: okay, it's probably too early to speak of "dying". Also, with crazy C# people on board, it's not likely JS is going to get really Pythonic. So there's likely to still be a niche for clean -> dirty compiler.

@DomVinyard

This comment has been minimized.

Copy link

DomVinyard commented Jul 11, 2016

@ibmua

Edit: actually, looking at ECMAScript6, it seems like CoffeeScript is going to die anyway. It would be hard for Coffee to catch up with all of the new features coming out as fast as they do

A language has one feature - the ability to write high-quality, maintainable code. To my eyes coffeescript still does that better than any language on the planet for a very large number of real-world things that real-world developers are actually tasked with working on.

@anodynos

This comment has been minimized.

Copy link

anodynos commented Jul 11, 2016

I agree 100% is great, its syntax is so neat, very readable and expressive and so on.
AND WE BLOODY MISS IT VERY MUCH, cause we have to look ahead and work with ES6 (which BTW has copied many of its synatic features from coffeescript).

IMHO Coffeescript@1.x has served its purpose, its time for CoffeeScript@6 or CoffeeScript@2015 or whatever, that targets ES6 and stop worrying about backwards compatibility with coffeescript@1.x that much.
Instead, target CoffeeScript@6 to be compatible & close with ES6, following the great mantra "Its just javascript" to "Its just ES6".

I wrote this to initiate the community #4078 (comment) I hope it get things moving.

PS: and yes, let should matter for CoffeeScript@6, mainly cause "its just ES6" (and makes life easier with binding to scopes etc). For CoffeeScript@1.x, I don't care and obviously it sholdn't change and break things.

@DomVinyard

This comment has been minimized.

@aurium

This comment has been minimized.

Copy link

aurium commented Jul 12, 2016

👍 Move to "Its just ES6"!

@GeoffreyBooth

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment