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
What is the point of the no-continue rule? #1103
Comments
|
continue, like break, is GOTO. There are countless articles on the internet about GOTO being harmful. In addition, this style guide discourages loops, which are the only places you'd need continue or break. |
|
I am going to disable this rule for my project. |
|
|
Loops themselves are banned by this gude; so there’s no use case for continue. |
|
I am using the latest version of the airbnb base config for eslint and it doesn't warn about |
|
Also, how would you generate an array with the numbers A..B without using a const range = [];
for (let x = A; x <= B; x += 1) {
range.push(x);
} |
|
The guide is far more expansive than the linter config; rest assured, it will one day warn on for loops, to match the guide text. The answer is you’d make a “range” abstraction, in which the implementation details are both irrelevant and fine to override linter warnings on, rather than writing a loop in your own code. |
|
Continue is not goto. That's like saying loops are goto. |
|
@SelaO no, it's not like saying that. Loops that use |
|
What's the difference between the following? and and Now how are these examples (which almost look like nested loops and keep all the jumps inside the scope of the loop) are the same as this? |
|
@SelaO I’m not sure what you’re asking. |
|
I actually don't currently use airbnbs rules, but am considering it after reading the discussion here. I was once a continue, break, return everywhere, kind of dude and felt very clever for leveraging those features, but really it doesn't help your team. The less bouncing around you have to do when reading code the better. continue makes code harder to read for other people -you might not see why from your own code, but imagine working on a large codebase with lots of loops and if statements with continues scattered about... Do you really want to maintain code you can't read top to bottom in one shot? Bite the bullet, learn to write easier to read code even if it's initially harder to do. Just for reference, in Scala the last line of code is automatically the return value if no return is specified and it's what they seem to encourage (disclaimer: I'm not a Scala expert, but just saying that there's things to be learned from other languages). |
|
@MegaArman See the example I gave above and tell me how continue is "bouncing around" like goto is.
That's an invalid statement, if the code already become such a mess, then not having continue and breaks won't help you, loops shouldn't be huge, just like functions shouldn't be huge. |
|
Yes, there are places where you should use loops. If you're doing it in front-end code, inside your main code-base, though, it might be a sign that there are other things that could be problems. In general, however, your arrays should be things that you want to iterate completely over. You may have a data structure problem, if you find yourself needing to drop out of a render loop early, for example. |
|
To clarify I really meant the traditional They are not in every language currently (Ex: Python doesn't have for(; ; ), some languages have always provided proper tail recursion instead of while) and haven't always been in languages. I cannot think of a case where I need them over forEach, map, recursion, etc. I think there's formal proofs somewhere out there that show they are not needed, but you can always find good alternatives on stackoverfow. Iteration can be done in more syntactically clean ways that are more akin to functional programming and less bug prone. There's some extreme cases where they might be more performant still -not sure, but nonetheless I can't imagine JavaScript is meant to concern such low level cases. @SelaO I'm not going to reply to the codebases discussion. Language considerations should always be worst case -the "well good programmers don't do that" argument is largely what let goto survive for two decades. Definitely suggest seeing some of Douglas Crockford's suggestions about using the most reliable subsets of languages. Brandon Eich who made JS also has suggestions in this regard (I recall him regretting "==" for starters). |
|
There are places where you can make a convincing argument for just about any language construct, even the ones that are almost universally panned. Ask a kernel programmer about goto, for example. Here's an easy example where a for loop is useful. Say you have 5000 records in an array, but you want the first 200 that fit a particular criteria. So you write a loop to search that array, and break once the limit is hit. Perfect use for a for loop. But you should abstract that to a separate function outside of your main logic. If you're doing that in a front-end environment, I'd also seriously consider if the front-end is the place to do that processing -- do you need all that data on the front-end, or should you be sending only the first 200 that fit from the back-end to begin with? That depends on your use case. Either way, it shouldn't be in the main logic. |
|
Or you write an abstraction using .some or .find, no loop required. Totally agree with the rest of the last comment tho. |
|
.... .some or .find return a true/false or a single item . . i could see ways to use them to construct something, then break, but i feel like that would be abusing them in a way that doesn't make sense, for the sake of not using a traditional loop. (feel free to provide an example, in case there's something i'm not considering) |
|
It’s not abusing them, it’s using them - iteration is best expressed with iteration methods, not with loops. |
|
I'd like to see an example of using .some or .find to return a limit 200 of a larger array, that doesn't look like abusing them, if you've got a minute to teach me. :-) |
function take(arr, predicate, count = Infinity) {
const taken = [];
arr.some((item, ...args) => {
if (predicate(item, ...args)) {
taken.push(item);
}
return taken.length >= count;
});
return taken;
}Obviously you may consider that “abuse” - that’s subjective - i do not; i consider that a perfectly reasonable usage of |
|
Yeah, that is exactly what I had imagined when I asked. It's unfamiliar, creative usage, and I'll need some time to think on that. :-) I've warmed up to most everything in here over time, though. I really appreciate you taking the time to provide examples for such things, it has definitely improved my creativity and expression of code. |
I feel like I've seen this repeated far too many times across multiple issues. Would it be worth adding That said, discussion around banning loops always revolves around |
|
Eventually we’ll be adding those rules, but we’d ever want to remove no-continue or anything similar; even when using loops, using continue is GOTO. |
Lets look at an example. Using continue: Using if: I'm guessing you'd say that the continue example is like GOTO because: it snaps the point of execution (from line-3 in example 1 to line-2, for the next iteration) The thing is, "snapping the point of execution" is also happening in example 2. You're "snapping the point of execution" from line 2 to line 4. "Okay, but in the second example, your always snap in the same direction -- you always snap downward, to directly after the block's braces." The thing is, you can make a similar statement about continue: "Okay, but you always snap in the same direction -- you snap upward, to the first line of the loop." In other words, both continue statements and if statements snap execution, and they do so in a consistent way: one always snaps up, the other always snaps down. Conclusion: I don't see how the continue statement is significantly more "like a GOTO" than an if statement. Okay, however:
Both of them:
I'd like to see a list of properties of continue statements which make them equivalent to GOTO statements, which are not shared by loops or if statements. I know there are some, but I don't believe them to be substantial enough to say "continue is GOTO" but "if statements are nothing like GOTO". |
|
@Venryx conditional branching isn't the same as goto, conceptually - even if under the hook it's all jumping to different CPU instructions.
|
But a regular loop fits that description as well. When you reach the end of a for loop block, it breaks the top-down flow of code-execution -- you have to scan back up to the top of the loop to find the next execution point. We don't consider loops (or forEach) to be as bad as GOTO though, and that's because:
And
In other words, if If there is a different reason for calling = = = = = = = = = = You do add:
The concern here could be that, If that's the core reason that Yet, the airbnb style guide indicates that it's fine to have multiple return statements in the same function. There are a few examples on the homepage, such as: https://github.com/airbnb/javascript#variables--define-where-used = = = = = = = = = = In summary, I think there were two criticisms of
My response was that:
The reason |
|
For me, I ignore the rules that encourage multiple return statements, and I stick to a rule that I've held much longer: There should be at most (with some potential exceptions) two points of exit to a function -- throw at the top, return at the bottom. Of course, we can't always throw when we need to exit early, but in general: early exit as quick as possible at the top, otherwise flow should reach the bottom. Your opinions may differ, and of course, this is the AirBnB style guide, not ericblade's style guide. but if i were to write one, it would be quite similar to this one :-) |
|
Yep, and I can understand that line of thinking. My intention's not to change the airbnb style, and I normally don't comment so long on these things. The main reason I responded was because of the insistence that kept being made that "continue is GOTO". In my opinion, that designation is lazy and misleading, because several of the negative properties of GOTOs are not in fact shared by So basically, the "continue is GOTO" rhetoric is what got me riled up (like a false assertion at a political rally), and if that one point were retracted I'd be content. |
|
Totally agree. Advocating against |
|
Let say I am trying to stick to the rule of no GOTO as suggested and I have a piece of code as follows what can I use to make the code more readable?
|
|
Deep down inside, all the control flow statements are goto's. Saying that break and continue are goto's, is not enough to justify the rule, because we can say the same for |
|
What things are “deep down” isn’t relevant; deep down, a gasoline-based car is a bunch of explosions, but a car isn’t an explosion. |
|
That's absolutely true. Therefore saying that we shouldn't use cars because they are moving by exploding - is not a valid statement. I mean all the criticism of goto's goes from the times of BASIC/assembly/etc, where goto is a source of disaster indeed. in JS, and especially for Anyway I get that general direction for airbnb is to move to immutable functional code, and for that loops dont fit well. Thanks for the quick response. |
This comment has been minimized.
This comment has been minimized.
|
@dcbrwn sure. the general rule would be "don't use explosions", just like it's "don't use goto" - but things that are "basically goto" but aren't actually goto aren't included in that prohibition. |
Exactly. Which is why break and continue should be allowed, just as if statements, return statements, loops, and function calls are. In other words, it has yet to be shown how break and continue are "more like goto" than those other elements above. The reasons so far provided have been discussed at length above, and shown to be present in those other allowed elements, so they are not valid as distinguishers between break/continue and those other elements. To format that more explicitly, it's being claimed that:
But when challenged to say how break and continue are "more like goto", the reasons provided do not stand up to scrutiny because those reasons/properties are present in if statements, return statements, loops, and function calls as well. Again, it's fine if AirBnb wants to prohibit an element in their style guide, that's their prerogative. The core issue most of us have is with the false claim that "break/continue is goto". It's not! (anymore so than those other elements) If you want to claim that it is, then show us what property break/continue has that makes it like goto, that is not shared by any of those other elements. (for example, |
|
Still there is no evidence that using "continue" is making code less testable and less maintainable for JavaScript. Saying that there are thousands of articles on the internet which say "continue is bad" is like saying that "snakes have legs" cause you have seen it on the internet from the newspaper named "Daily Testicle". I am re-closing it by applying the information there were no valid arguments on why "continue" should not be used. |
|
Ohh, so many uses for for-loops that cannot be done with these functional loops. I use the AirBnb rules, but this first thig I turn off is |
|
Sorry to continue commenting on this thread and this is opinionated but come on @ljharb, do you really prefer a bunch of possibly nested if-elses instead of simple continue statement? how can that be more readable ? for(const item of data) {
if (x) {
// ...
} else {
if (y) {
// ...
} else {
// ...
}
}
}vs for(const item of data) {
if (x) {
// ...
continue
}
if (y) {
// ...
continue
}
// ...
} |
|
@ricardodnb yes. If you provide a non-contrived case, I’ll show you what i find most readable - and it’s not going to be a loop at all. |
|
@ricardodnb a concrete example would allow for better explanations, but but your code could look something along these lines. const itemsX = items.filter(isX).map(toItemX);
const itemsY = items.filter(isY).map(toItemY);Avoiding loops means breaking down your operations into smaller steps. It makes your code more declarative than imperative. |
|
I'll add few points here for people that look for better justification of this rule:
|
|
All control flows are masqueraded There are cases when we have to use Still, I believe there are many cases when we can use |
|
yes, it does mean that - performance overrides clarity here, and you’d use an eslint override comment to explain why you used a worse pattern. |
|
I try to keep all of the things that require eslint overrides close together in the lower level functionality area of the code. Anything high level especially at UI level, I want to be following this guide as close as possible. |
|
If you're considering performance at a given location, you're already thinking at a very, very low level. |
The official ESLint docs say "When used incorrectly it makes code less testable, less readable and less maintainable."
Does Airbnb agree with the above reasoning? If so, can you give an example of how
continuemakes code less testable and less maintainable?(I'm not arguing against it btw, just want to understand why.)
The text was updated successfully, but these errors were encountered: