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

enabling eslint's prefer-const rule #3118

Closed
misterdjules opened this issue Sep 29, 2015 · 31 comments
Closed

enabling eslint's prefer-const rule #3118

misterdjules opened this issue Sep 29, 2015 · 31 comments
Labels
discuss Issues opened for discussions and feedbacks.

Comments

@misterdjules
Copy link

This conversation started here: #3036 (comment).

It seems there's an implicit rule of preferring const over var for variables that hold values that never change, which makes sense. It's been mentioned in #1243, and seems to be enforced during code reviews.

However, it's not currently caught by our eslint setup. To paraphrase #3036 (comment), I'm not a big fan of having implicit coding style rules. It becomes frustrating for maintainers to enforce them every time, and for contributors because they don't have any tool to check that their code complies to the guidelines.

However, using the prefer-const rule does not apply to function-scoped variable declarations, so we would need to use the no-var eslint rule too and use let instead of var, which I assume would require a significant amount of work.

#1243 mentions that let's V8's implementation had performance at some point.

My question thus is: is moving from var to let and enforcing const with eslint worth investigating now?

/cc @nodejs/tsc

@misterdjules misterdjules added the discuss Issues opened for discussions and feedbacks. label Sep 29, 2015
@mscdex
Copy link
Contributor

mscdex commented Sep 29, 2015

FWIW I just ran a benchmark on jsperf.com and it looks like let performs the same as var with v8 4.5.

@trevnorris
Copy link
Contributor

please done use those. they're useless on a benchmark of this scale.

Right now in the general case let performance is comparable to var. But there are specific scenarios where it completely fails and is much slower. One such case is when a class is defined within the same scope where let is used. Here's the generated output from v8's optimizing compiler, and you can see the let case generates 100+ more lines of instructions for the simple for loop: https://gist.github.com/trevnorris/73b0d8c0e831d6e48caa

@Fishrock123
Copy link
Contributor

The argument against this is to slowly move to using const where possible, rather than making a massive diff that screws up git blame. (I am favor of moving slowly to it)

@trevnorris
Copy link
Contributor

@Fishrock123 makes an appropriate point. This is how we've migrated code in the past. Unless the code was breaking for some reason, we've generally left these types of changes to organically migrate through the code base.

Though how do we then address new PRs? I think it's a deterrent for new contributors when they are barrage with nit style comments. In the past I've made changes to the commits before landing it. Though that has always been for minor things (e.g. trailing whitespace). Not sure how we'd feel about doing the same for the many variable declarations.

@Trott
Copy link
Member

Trott commented Sep 29, 2015

If no-var is off the table (and it sounds like it is, at least for the foreseeable future), turning on prefer-const by itself only flags five lines in four files of the current code base. I'm actually all for turning it on.

@Fishrock123
Copy link
Contributor

@Trott could you get us a diff? :)

@thefourtheye
Copy link
Contributor

➜  io.js git:(master) ✗ make lint
./node tools/eslint/bin/eslint.js src lib test --rulesdir tools/eslint-rules --reset --quiet

lib/cluster.js
  294:12  error  `debugPort` is never modified, use `const` instead  prefer-const

test/parallel/test-buffer-zero-fill-reset.js
  17:6  error  `ui` is never modified, use `const` instead  prefer-const

test/parallel/test-http-flush-headers.js
  13:6  error  `req` is never modified, use `const` instead  prefer-const

test/sequential/test-child-process-fork-getconnections.js
   9:6  error  `sockets` is never modified, use `const` instead  prefer-const
  45:6  error  `sockets` is never modified, use `const` instead  prefer-const

✖ 5 problems (5 errors, 0 warnings)

make: *** [jslint] Error 1
➜  io.js git:(master) ✗ git --no-pager diff
diff --git a/.eslintrc b/.eslintrc
index cf1f768..eeaaff4 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -70,6 +70,8 @@ rules:
   # require space after keywords, eg 'for (..)'
   space-after-keywords: 2

+  prefer-const: 2
+
   # Strict Mode
   # list: https://github.com/eslint/eslint/tree/master/docs/rules#strict-mode
   ## 'use strict' on top

@cjihrig
Copy link
Contributor

cjihrig commented Sep 30, 2015

Since this is almost entirely in tests, I'm +1 for the change.

@Fishrock123
Copy link
Contributor

I have a hard time believing those are the only spots, could it be the rule isn't that good at picking things up?

@Trott
Copy link
Member

Trott commented Sep 30, 2015

@Fishrock123 The rule only applies to 'let' declarations and not 'var'. 'const' would change the scope of a 'var' but not a 'let'.) So that's why the number of changes would be small.

@rvagg
Copy link
Member

rvagg commented Sep 30, 2015

The argument against this is to slowly move to using const where possible, rather than making a massive diff that screws up git blame. (I am favor of moving slowly to it)

This is frustrating because it leaves us in the land of the implicit which is terrible unless everyone's on the same page, which we are not. I don't recall a discussion where it was agreed that we were moving to const slowly and now we are in a situation where a subgroup of the collaborators push on this when they are reviewing PRs and others don't leading to uncertainty, like in #2411, which is an unpleasant experience for contributors (and collaborators!).

Either it's explicit and stated somewhere (do we need a styleguide we can bikeshed over?) or it should be left up to contributors and not haggled over by reviewers.

@ChALkeR
Copy link
Member

ChALkeR commented Sep 30, 2015

If no-var is off the table (and it sounds like it is, at least for the foreseeable future), turning on prefer-const by itself only flags five lines in four files of the current code base. I'm actually all for turning it on.

I'm +1 for turning prefer-const on in such situation.

Above all, it gives a clear hint to any person who is looking on the code that the variable isn't going to be re-assigned. It has at least that advantage over var.

@thefourtheye
Copy link
Contributor

@rvagg I am sorry. That was me who suggested it in #2411. I just thought its more appropriate to use const there. I'll hold off on suggesting it till we reach a conclusion.

@jasnell
Copy link
Member

jasnell commented Sep 30, 2015

I'm with @rvagg on this. The current approach is haphazard at best. We need
consistency. I've got no problem with moving to const systematically but if
we're doing so it needs to be something we're all doing.
On Sep 29, 2015 8:58 PM, "Rod Vagg" [email protected] wrote:

The argument against this is to slowly move to using const where possible,
rather than making a massive diff that screws up git blame. (I am favor of
moving slowly to it)

This is frustrating because it leaves us in the land of the implicit which
is terrible unless everyone's on the same page, which we are not. I don't
recall a discussion where it was agreed that we were moving to const slowly
and now we are in a situation where a subgroup of the collaborators push on
this when they are reviewing PRs and others don't leading to uncertainty,
like in #2411 #2411, which is an
unpleasant experience for contributors (and collaborators!).

Either it's explicit and stated somewhere (do we need a styleguide we can
bikeshed over?) or it should be left up to contributors and not haggled
over by reviewers.


Reply to this email directly or view it on GitHub
#3118 (comment).

@silverwind
Copy link
Contributor

We could switch everything to const pretty easily I think:

  1. find/replace all var with let.
  2. now, prefer-const should warn on all places that could possible be turned into const.
  3. fix the cases, maybe through auto-fixing of newer eslint versions.

Also, let's not forget #2286.

@chrisdickinson
Copy link
Contributor

@silverwind:

  1. find/replace all var with let.

That has the potential to break things due to block scoping — I might suggest doing a sweep with jik or some other AST search tool to find vars declared inside blocks and replace those, first. Otherwise this sounds like a pretty good plan.

@rvagg:

Either it's explicit and stated somewhere (do we need a styleguide we can bikeshed over?) or it should be left up to contributors and not haggled over by reviewers.

If possible, I'd prefer we go the standard route: if our linting tool doesn't complain about incoming JS, neither should we. If one sees a nit while reviewing, one should write a rule and propose adding it to the linter in a new PR.

@silverwind
Copy link
Contributor

@chrisdickinson I think these scope issues can be caught beforehand through the block-scoped-var rule. Just enabling shows 111 issue in the repo, oh my.

@Fishrock123
Copy link
Contributor

if our linting tool doesn't complain about incoming JS, neither should we.

Well, not necessarily, not everything can be caught by linters. :)

@trevnorris
Copy link
Contributor

Forcing let is a definitive -1. I've already demonstrated there are corner cases where let fails on performance. To take a potential performance hit so we can feel warm and fuzzy about our code style is unacceptable.

@chrisdickinson
Copy link
Contributor

@silverwind:

It seems like that's worth a cleanup issue!

@Fishrock123:

Well, not necessarily, not everything can be caught by linters. :)

True! Style concerns can be caught, though — eslint is pretty expressive when it comes to making new rules, and building these opinions into our linter is just about the only way to make sure they're consistently applied in the long run. (Plus, leaning on the linter saves reviewer time, and reduces back-and-forth in the contribution process!)

@trevnorris:

To take a potential performance hit so we can feel warm and fuzzy about our code style is unacceptable.

Given that the codebase will survive multiple versions of V8, how long do we expect to see the let perf hit survive? How big is the perf hit, if we take it — do we hit those corner cases? If so, can we rework to avoid those corner cases and add lint rules around them in the meantime?

@trevnorris
Copy link
Contributor

@chrisdickinson

Given that the codebase will survive multiple versions of V8, how long do we expect to see the let perf hit survive?

We don't code for what will be optimized, but what is optimized. And I have no idea when it will be fixed.

How big is the perf hit, if we take it — do we hit those corner cases? If so, can we rework to avoid those corner cases and add lint rules around them in the meantime?

In for loops it is substantial, and I don't have a conclusive list of every case where this is applicable. As you can see from my test case, the optimizing compiler completely fails. Generating almost twice the number of instructions. This is enough to turn from an annoyance to a blocking issue.

Even if we don't hit a case today, and we don't know all cases where it may appear, what will happen if a change comes in that hits it after this lands?

I'm +1 on making everything const that can be, but not at the cost of forcing let down everyone's throat.

thefourtheye added a commit to thefourtheye/io.js that referenced this issue Oct 4, 2015
As per block-scoped-var rule, the variables which were used before
declaration or declared within a block and used outside are trated
as linter errors.

Refer: nodejs#3118
thefourtheye added a commit to thefourtheye/io.js that referenced this issue Oct 4, 2015
As per block-scoped-var rule, the variables which were used before
declaration or declared within a block and used outside are trated
as linter errors.

Refer: nodejs#3118
@Fishrock123
Copy link
Contributor

Question: why can't we prefer const to var where applicable instead of let?

thefourtheye added a commit to thefourtheye/io.js that referenced this issue Oct 25, 2015
Description from: http://eslint.org/docs/rules/prefer-const.html

If a variable is never modified, using the `const` declaration is
better. `const` declaration tells readers, "this variable is never
modified," reducing cognitive load and improving maintainability.

Refer: nodejs#3118
PR-URL: nodejs#3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
thefourtheye added a commit to thefourtheye/io.js that referenced this issue Oct 25, 2015
As per the `prefer-const` eslint rule, few instances of `let` have been
identified to be better with `const`. This patch updates all those
instances.

Refer: nodejs#3118
PR-URL: nodejs#3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
thefourtheye added a commit that referenced this issue Oct 27, 2015
Description from: http://eslint.org/docs/rules/prefer-const.html

If a variable is never modified, using the `const` declaration is
better. `const` declaration tells readers, "this variable is never
modified," reducing cognitive load and improving maintainability.

Refer: #3118
PR-URL: #3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
thefourtheye added a commit that referenced this issue Oct 27, 2015
As per the `prefer-const` eslint rule, few instances of `let` have been
identified to be better with `const`. This patch updates all those
instances.

Refer: #3118
PR-URL: #3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
rvagg pushed a commit to rvagg/io.js that referenced this issue Oct 29, 2015
Description from: http://eslint.org/docs/rules/prefer-const.html

If a variable is never modified, using the `const` declaration is
better. `const` declaration tells readers, "this variable is never
modified," reducing cognitive load and improving maintainability.

Refer: nodejs#3118
PR-URL: nodejs#3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
rvagg pushed a commit to rvagg/io.js that referenced this issue Oct 29, 2015
As per the `prefer-const` eslint rule, few instances of `let` have been
identified to be better with `const`. This patch updates all those
instances.

Refer: nodejs#3118
PR-URL: nodejs#3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
@cjihrig
Copy link
Contributor

cjihrig commented Nov 12, 2015

Closing this as prefer-const was enabled in b0e7b36 and no-var was shot down.

@cjihrig cjihrig closed this as completed Nov 12, 2015
thefourtheye added a commit that referenced this issue Dec 29, 2015
As per the `prefer-const` eslint rule, few instances of `let` have been
identified to be better with `const`. This patch updates all those
instances.

Refer: #3118
PR-URL: #3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
thefourtheye added a commit that referenced this issue Dec 29, 2015
Description from: http://eslint.org/docs/rules/prefer-const.html

If a variable is never modified, using the `const` declaration is
better. `const` declaration tells readers, "this variable is never
modified," reducing cognitive load and improving maintainability.

Refer: #3118
PR-URL: #3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
MylesBorins pushed a commit that referenced this issue Jan 19, 2016
As per the `prefer-const` eslint rule, few instances of `let` have been
identified to be better with `const`. This patch updates all those
instances.

Refer: #3118
PR-URL: #3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
MylesBorins pushed a commit that referenced this issue Jan 19, 2016
Description from: http://eslint.org/docs/rules/prefer-const.html

If a variable is never modified, using the `const` declaration is
better. `const` declaration tells readers, "this variable is never
modified," reducing cognitive load and improving maintainability.

Refer: #3118
PR-URL: #3152
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Сковорода Никита Андреевич <[email protected]>
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
@zackster
Copy link

zackster commented Feb 7, 2020

@chrisdickinson

Given that the codebase will survive multiple versions of V8, how long do we expect to see the let perf hit survive?

We don't code for what will be optimized, but what is optimized. And I have no idea when it will be fixed.

How big is the perf hit, if we take it — do we hit those corner cases? If so, can we rework to avoid those corner cases and add lint rules around them in the meantime?

In for loops it is substantial, and I don't have a conclusive list of every case where this is applicable. As you can see from my test case, the optimizing compiler completely fails. Generating almost twice the number of instructions. This is enough to turn from an annoyance to a blocking issue.

Even if we don't hit a case today, and we don't know all cases where it may appear, what will happen if a change comes in that hits it after this lands?

I'm +1 on making everything const that can be, but not at the cost of forcing let down everyone's throat.

I know this is closed, and there may be a more relevant discussion (someone please link?) but my solution is:

// let is much faster than const in for..of loops
// eslint-disable-next-line prefer-const

@mikeal
Copy link
Contributor

mikeal commented Feb 7, 2020

we should ping folks on the V8 team to get an idea of why this is still an issue. it’s been several years now and this syntax isn’t exactly new. it might just be a matter of adding this case into some of the perf suites JS VM’s are running.

@Trott
Copy link
Member

Trott commented Feb 7, 2020

we should ping folks on the V8 team to get an idea of why this is still an issue. it’s been several years now and this syntax isn’t exactly new. it might just be a matter of adding this case into some of the perf suites JS VM’s are running.

It has been resolved for some time. Loops using let have comparable performance to loops using var.

@mikeal
Copy link
Contributor

mikeal commented Feb 7, 2020

It has been resolved for some time. Loops using let have comparable performance to loops using var.

what about const?

@Trott
Copy link
Member

Trott commented Feb 7, 2020

It has been resolved for some time. Loops using let have comparable performance to loops using var.

what about const?

Doh! I did the "see what I expected to see and not what is actually there on the screen in front of me" thing. Sorry. I don't know the answer to that.

@zackster
Copy link

zackster commented Feb 7, 2020

It has been resolved for some time. Loops using let have comparable performance to loops using var.

what about const?

Doh! I did the "see what I expected to see and not what is actually there on the screen in front of me" thing. Sorry. I don't know the answer to that.

I don't know what sort of credibility jsperf has with the team here, but I created a test earlier today that addresses this question:

https://jsperf.com/foreach-vs-for-of-performance-test/9

After my initial run, let was definitively faster. After testing multiple times, I see variance between test runs. Sometimes let is faster and sometimes const is faster. It may vary through the amount of processing within the loop?

@mikeal
Copy link
Contributor

mikeal commented Feb 7, 2020

making sure @rvagg sees this so that he can stop frowning at my aggressive const usage 😜

@rvagg
Copy link
Member

rvagg commented Feb 11, 2020

bleh, I'm just a slave to whatever standard tells me these days

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discuss Issues opened for discussions and feedbacks.
Projects
None yet
Development

No branches or pull requests