Skip to content
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 · 21 comments
Closed

Why "let" is important #4015

babakness opened this issue Jun 18, 2015 · 21 comments

Comments

@babakness
Copy link

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
Copy link

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
Copy link
Collaborator

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

@tigerhawkvok
Copy link

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

@vtenfys
Copy link

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

@vtenfys
Copy link

vtenfys 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
Copy link

@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
Copy link

Fonger commented Mar 16, 2016

+1

@samuelhorwitz
Copy link

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
Copy link
Contributor

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
Copy link
Contributor

-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
Copy link
Contributor

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
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
Copy link
Contributor

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
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
Copy link

@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
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 [email protected] has served its purpose, its time for CoffeeScript@6 or CoffeeScript@2015 or whatever, that targets ES6 and stop worrying about backwards compatibility with [email protected] 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 [email protected], I don't care and obviously it sholdn't change and break things.

@DomVinyard
Copy link

@aurium
Copy link
Contributor

aurium commented Jul 12, 2016

👍 Move to "Its just ES6"!

@GeoffreyBooth
Copy link
Collaborator

@0ui0
Copy link

0ui0 commented Sep 21, 2022

但是但是,为什么不彻底放弃var而使用let呢?
coffee完全没有必要同时支持var和let。全部改成let不是更好吗——同时也符合coffee的简约高效的理念
既然都已经做了变量提升了,let完全可以代替var实现全部的工作
而在最新js里面,所有的变量都可以用let来定义而可以完全放弃使用var

translate from google:
But yet, why not abandon var altogether and use let?
There is absolutely no need for coffee to support both var and let. Wouldn't it be better to change it all to let — and it's also in line with coffee's philosophy of simplicity and efficiency
Now that the variable has been lifted, let can completely replace var to achieve all the work
In the latest js, all variables can be defined with let and var can be completely abandoned

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests