I see these warnings a lot, but I work on a team that does not ever use semicolons, and hasn't for years. It is simply not a problem, I don't know what else to say. And I have come to really dislike the syntactic noise the semicolons add.
We don't use semicolons at all. We do use comma-first!
About the concatenation, we use Browserify which bundles our code for us. The beginning of a Browserify bundle is a definition for the `require` function.
I use
var foo = 'bar';
but at my place of work they insist on no semi-colons and comma first. I want to tell them this is stupid but I need solid facts. anyone care to expand.
I never tire of comparing Lua with Javascript, and this is another one of those cases. A semicolon in Lua is simply a character which is illegal to place anywhere but the end of a statement.
There is no insertion stage, the language parses just fine on a single line, or any whitespace you choose. Semicolons are vanishingly uncommon in Lua code, but if you want to compress a couple statements onto a line, they signal your intention.
Lua is like what Javascript would be if it were: based on Pascal, written by resource-constrained Brazilians, and capable of making backwards-incompatible changes. Though with the de-facto fork between Lua and LuaJIT, the latter has become much more difficult, as it should at some point in the maturing of a language.
If anything, Javascript suffered because it had large corporations invested in it. The guys at PUC-Rio were never beholden to corporate sponsors as far as I know, and thus free to break compatibility.
Why does corporate sponsorship have anything to do with it? Any browser that gained non-trivial market share would have felt the same "don't break the web, or you lose market share" pain that the bigs felt.
KHTML only started getting adoption via the Safari fork because Dave Hyatt and others implemented real-world quirks (e.g., residual style), prior to webkit.org.
Gecko had the benefit of `if (document.all) { IE code here } else { Netscape code here }` patterns from the '90s, but we had to add quirks to gain share with Firefox (m/b, then Phoenix) in 2002 and onward: innerHTML, undetected document.all emulation, and of course XHR.
Corporations have nothing to do with it. Any evolving ecosystem with a huge corpus of popular content begets terrific selection pressure against breaking backward compatibility.
I'm not sure Lua is that different than Javascript here. What is a semicolon in Javascript if not a character you can only place at the end of a statement? There are some similar ambiguities in the Lua parser if you insert a newline into the middle of a statement.
The difference is that Lua is quite a bit fussier about statements versus expressions, and won't allow things like "x() or y()" as statements. So the Javascript example, translated to Lua, wouldn't compile with or without a semicolon.
There are no such ambiguities. Here is the complete Lua grammar. You may confirm for yourself that semicolons are always optional and that newlines have no semantic meaning.
This creates the same error. Deleting the newline gives you a new, different error. A semicolon give the correct behavior. It's the edge case that the Lua grammar can't resolve, and it does the sensible thing, by refusing to compile.
Also note that it is a compile-time error that is described as an ambiguity. It isn't a silent thing that bites you later, as in Javascript, and the difference is huge.
[added] I'm not sure what you mean re "x() or y()" the following compiles fine:
function t()
return true
end
function f()
return false
end
if t() or f() then
print "logic"
end
function fn(y)
return y + 1
end
x = fn (1)
print(x)
Works.
function fn(y)
return y + 1
end
x = fn
(1)
print(x)
Doesn't work. The only difference is a newline; clearly newlines do matter. (And I specifically added the space to show that it's not simply whitespace that matters.) This is, of course, documented in the manual.
For the original example, I meant that expressions are not valid statements.
true or false
On a line by itself does not compile. It's an expression, but not a statement.
This is one of the things I like about Lua, that it prefers explicit over clever, although sometimes the difference between statements and last statements gets annoying. I'd like to do
function fn()
return nil
-- more code, stubbed out for testing
end
But you can't have code after return. Instead it needs to be wrapped with if true.
Quite so. I didn't consider this aspect in my first post because it's a syntax error, not an ambiguity; unlike Javascript, Lua won't resolve your ambiguity for you.
I share your feeling about Lua being a bit more statement oriented than I'd like. Have you checked out Moonscript?
Not using semicolons has never be a problem for me.
Just like everyone else I put them where they are needed. I don't put them where they aren't needed, because they aren't needed and adding pointless syntax noise is dumb. If I forget to put them in somewhere they are needed, as anyone might do, I add them. No problems.
See http://inimino.org/~inimino/blog/javascript_semicolons, hope it helps. The minimal semicolon style has only a few rules and many people find it easier to use and remember. I've used it but my C hacker habits go against it. It's a fine style.
Wow neat,yeah, js devs are going to call anyone out that doesnt use semicolons.
Question : If you ever wanted 1 breaking change in the language, and somehow it would be possible (I know it wouldn't) what feature would you change ? Again just 1 feature.
== is not slow when types of operands (in typeof sense) match -- then it's equivalent to ===.
You'll find a lot of == out there, or would if only Google code search were still up.
Hey, I got to answer with the one thing I'd change. There are more than a few, and I had to pick. Losing the equivalence relation I had in the first ten days (apart from NaN != NaN of course) was my pick.
I haven't used semicolons in JavaScript in places they're not necessary for years now - the only rule that's mattered in practice [1] is lines starting with [ or (.
[1] This is a white lie - there is one additional vital ASI rule, but you already know it - don't put a linebreak after a return statement.
Except that there actually was a problem in this case as evidenced by the surrounding discussion. If the code had been written
with a semicolon (or an if statement!) from the beginning, there never would have been a problem. You can argue that it wouldn't have been a problem if JSLint had been written "correctly" from the beginning, but every tool has bugs.
Code defensively, and don't get fancy unless you need to. You're not just complying with the language spec, you are communicating your intent to the other developers who will read and maintain your code.
> Code defensively, and don't get fancy unless you need to. You're not just complying with the language spec, you are communicating your intent to the other developers who will read and maintain your code.
Leaving out semicolons actually makes the intent more clear. People run into problems with semicolon insertion when they try to create an unnamed expression, i.e:
["joe", "bob", "ann"].forEach(call);
If you use a no-semicolon style, though, you must name all your expressions, which makes your intent more clear:
var employees = ["joe", "bob", "ann"]
employees.forEach(call)
When I mentioned intent, I was mostly referring to the use of an "if" statement vs. short circuiting in the referenced example. But I mean it in general. Every code base is different, of course.
> If you use a no-semicolon style, though, you must name all your expressions.
I see the bug as JSLint's problem, not the code in question (though admittedly that was bad code). I agree that if your tooling requires semicolons you should be pragmatic and include them, but the reality is that parsers/minifiers are mostly built with the assumption that users won't always include semicolons, so that's a largely hypothetical situation.
I definitely agree that you should code defensively, but I'm just not seeing why adding semicolons where ASI kicks in is a practical
thing to do. I don't agree semicolons make the code more readable, but if there was such a case you should definitely add them. And everyone -
not just people who omit non-ASI'ed semicolons - will forget to include them where needed eventually, but that's not even an especially painful bug.
There are in my opinion more dangerous style choices that are ignored, like daisy-chained declarations. Writing
var x = 1,
y = 2,
z = 3
is a pretty lousy idea, as it unnecessarily couples the meaning of several lines of code together, and makes code harder to edit. If you unthinkingly truncate the first line you get
y = 2,
z = 3
a pair of global assignments! That could be a really pernicious bug if you have supposedly 'local' variables sharing the name with an existing global object. It's better to be defensive and write
var x = 1
var y = 2
var z = 3
if only for editability.
My point is that the strong feeling on 'Semicolons' vs 'No Semicolons' is largely a product of focusing bias (http://en.wikipedia.org/wiki/Anchoring); to the extent that it is a problem either way it is a minor one, evidenced by the fact that similar stylistic dangers (see above) are either unfairly ignored or rightly not fretted over.
tl;dr
I don't see a practically valuable reason to use them where ASI does the work for you, semicolon vs semicolon is barely a practical issue either way.
I am - I think - looking at this from a safe-by-default perspective. Everyone will forget to include a semicolon eventually as you pointed out, but shouldn't your default strategy be the safest one?
Maybe I am missing something. There are obviously cases where missing semicolons do cause problems. Is there a situation in which adding a semicolon where allowed causes problems or increases the chances of introducing a bug in the future?
What's the default, though? If I try adding semicolons and over-do it, probably I'm ok. But I might make an empty statement while loop body -- an iloop. And being human, I will inevitably leave a crucial semicolon out.
I agree with the parent post. You can go wrong with either C-like or "NPM house" style. Linters are important, moreso in JS than in modern C and C++ (back to the future; I remember old C, which was wildly permissive).
Pick your poison, study it well and dose carefully.
Now I know where my bias comes from: I did not consider going wrong with semicolons in C. Perhaps that is because C is my oldest friend. You are of course correct that the C style is not perfect either. Thanks for the reply.
A while back I think there was a github issue where the authors of some popular library refused to use semi-colons and it was causing all sorts of problems for people. I can't remember what library, but it was funny watching hundreds of people comment on the issue to say "Just use the damn semi-colons!"
This has never really struck me as a good argument for using or not using semi-colons.
1.) The non-use of semi-colons was perfectly valid JavaScript in that case.
2.) The parser portion of the minifier from Douglas Crockford did not account for this particular case.
3.) It became a flamewar because Douglas Crockford called it out as an error in a very ham fisted way, and the developer responded in kind. If a c++ compiler chokes on valid input, then a bug is sent to the compiler maintainer, and the developer rewrites their code until the bug is fixed.
4.) And that's exactly what happened. The minifier parser was fixed, and the Bootstrap code was updated to work around the parser error.
5.) The reason for not using semi-colons was legitimate. This is a library with a large audience, and not everyone has a minifier/compresser available. It was an easy win for something that wasn't needed. That's exactly the type of stuff that I want when I lean on 3rd parties.
So for me this could have gone a completely different way had the two parties been more civil:
BS: Hey DC, can you fix your minifier, it looks like it's busted on this valid input.
DC: Hrm, looks like your right. I'll fix it up, why don't you add a semi-colon in there until I can get it fixed
BS: Hey, no problemo. Done. I look forward to your fix!
So this is really more of a story about being professional to your fellow developers than anything that has to do with semi-colons or not.
You can get by perfectly fine without semicolons. Going further, if you're using a linter like eslint, I think you can even configure it to give you a warning when the missing semicolon could cause problems.
The biggest problem I have is the "leaders" forgetting the beginners. The whole semicolon thing took JS from "end a statement with a ;" to:
THE RULES:
In general, \n ends a statement unless:
1. The statement has an unclosed paren, array literal, or object literal or ends in some other way that is not a valid way to end a statement. (For instance, ending with . or ,.)
2. The line is -- or ++ (in which case it will decrement/increment the next token.)
3. It is a for(), while(), do, if(), or else, and there is no {
4. The next line starts with [, (, +, *, /, -, ,, ., or some other binary operator that can only be found between two tokens in a single expression.
You have a junior dev now that's 2 steps behind because he's worrying about self-imposed "beatifying" edge cases instead of getting his code to run.
Unfortunately, it wouldn't be the Web sites failing; it'd be the browsers that decided to break backwards compatibility as users flee them on account of their favorite Web sites not working.
1. Modes are bad for implementations (this feeling was strong from V8 folks such as Erik Corry, but the new crew in Munich may not feel the same way), due to code branching burdens in testing and optimizing while keeping the complexity down.
2. Worse, what you propose would fork the language to double the testing burden for web devs (apart from engine implentors, item 1), since older browsers would ignore the useless expression statement.
Developers do not test all the combinations. Notice how a tower of M modes implemented via these string-expression-statement prologue directives can make a 2^M-fold testing burden.
As noted in item 1 ("new crew in Munich"), but these may both throw early errors, not let code get to runtime with different semantics (as ES5 strict did, due to arguments object and a few other changes), and use real pragma syntax, which will fail in old user agents.
How can real pragma syntax work on the Web? Only by using a compiler such as Traceur or Babel, otherwise old browsers would choke on the `use types;` unquoted directive.
Note how this can still complicate VMs, but it doesn't fork the test load for web developers. And if the type system results in early errors, plus better optimization with unforked runtime semantics, then the only burden on VM implementors is in the parser and type checker.
We shall see how this experiment works, but in no way is it like `"use stricter";`.
> use real pragma syntax, which will fail in old user agents
> We shall see how this experiment works, but in no way is it like `"use stricter";`.
I might be misunderstanding your comment, but stupidcar's link (http://www.2ality.com/2015/02/soundscript.html) mentions `"use stricter;"` and `"use stricter+types;"` as mode flags, although it also contains a big disclaimer ("The final version of SoundScript may look and work completely different").
Has the syntax been changed since February? If so, I'm interested in reading about it (but my Google-fu is failing); got a link?
That talk on which Axel Rauschmayer reported had some info from a presentation given by Andreas Rossberg of the V8 team to the last TC39 meeting. Here's the PDF:
1. A `"use stricter";` (originally "use sanity" :-/) prologue directive that would not change runtime semantics for code that passed all of static checks, (control flow dependent) dynamic exception-throwing tests, and runtime restrictions on object behavior.
2. A "SoundScript" mode, perhaps enabled by a `use types;` opt-in, or expressed some other way (per module?), that enables gradual type checking based on optional annotations inspired by TypeScript, but with TS-incompatible differences.
Mode 1 might fly with TC39, because it does not fork semantics for code that passes all checks. But for real-world code, there will be divergence when run on old vs. new browsers, so I expect push-back in TC39 on this mode being expressed by a useless string expression statement.
Mode 2 cannot be enabled by a fake-string-expression-statement "prologue directive".
Which programming language first used a semicolon as a statement delimiter? Did they choose the semicolon because it was easy to type on a QWERTY keyboard? Why not end a statement with a period like a sentence?
The real surprise here is that people still use JSMin, written by an opinionated grump who apparently does not understand the concept of using a real parser. Several superior alternatives exist. UglifyJS is better in every respect.
72 comments
[ 8.1 ms ] story [ 157 ms ] threadDo you use ; at end of var declarations? If not, beware; if so, do you use comma-first? Thanks for answers, just curious.
About the concatenation, we use Browserify which bundles our code for us. The beginning of a Browserify bundle is a definition for the `require` function.
always var foo = 'bar';
If you do comma first, I will automatically assume you're an idiot.
Two, stop making stupid assumptions.
Now is:
There is no insertion stage, the language parses just fine on a single line, or any whitespace you choose. Semicolons are vanishingly uncommon in Lua code, but if you want to compress a couple statements onto a line, they signal your intention.
Lua is like what Javascript would be if it were: based on Pascal, written by resource-constrained Brazilians, and capable of making backwards-incompatible changes. Though with the de-facto fork between Lua and LuaJIT, the latter has become much more difficult, as it should at some point in the maturing of a language.
Think Blizzard. WoW uses Lua heavily. Though I suspect that niche will eventually be taken away from Lua too, which is unfortunate.
But without corporate backing, we wouldn't have had so many amazing implementations, which helped with the adoption.
Even if Lua (or Lua-JIT) can still beat the crap out of them sometimes. Back then there wasn't even a contest.
KHTML only started getting adoption via the Safari fork because Dave Hyatt and others implemented real-world quirks (e.g., residual style), prior to webkit.org.
Gecko had the benefit of `if (document.all) { IE code here } else { Netscape code here }` patterns from the '90s, but we had to add quirks to gain share with Firefox (m/b, then Phoenix) in 2002 and onward: innerHTML, undetected document.all emulation, and of course XHR.
Corporations have nothing to do with it. Any evolving ecosystem with a huge corpus of popular content begets terrific selection pressure against breaking backward compatibility.
The difference is that Lua is quite a bit fussier about statements versus expressions, and won't allow things like "x() or y()" as statements. So the Javascript example, translated to Lua, wouldn't compile with or without a semicolon.
http://www.lua.org/manual/5.1/manual.html#8
print(foo) ("x"):len()
This creates the same error. Deleting the newline gives you a new, different error. A semicolon give the correct behavior. It's the edge case that the Lua grammar can't resolve, and it does the sensible thing, by refusing to compile.
Also note that it is a compile-time error that is described as an ambiguity. It isn't a silent thing that bites you later, as in Javascript, and the difference is huge.
[added] I'm not sure what you mean re "x() or y()" the following compiles fine:
and does indeed print "logic".For the original example, I meant that expressions are not valid statements.
On a line by itself does not compile. It's an expression, but not a statement.This is one of the things I like about Lua, that it prefers explicit over clever, although sometimes the difference between statements and last statements gets annoying. I'd like to do
But you can't have code after return. Instead it needs to be wrapped with if true.I share your feeling about Lua being a bit more statement oriented than I'd like. Have you checked out Moonscript?
Just like everyone else I put them where they are needed. I don't put them where they aren't needed, because they aren't needed and adding pointless syntax noise is dumb. If I forget to put them in somewhere they are needed, as anyone might do, I add them. No problems.
/be
Question : If you ever wanted 1 breaking change in the language, and somehow it would be possible (I know it wouldn't) what feature would you change ? Again just 1 feature.
You'll find a lot of == out there, or would if only Google code search were still up.
Hey, I got to answer with the one thing I'd change. There are more than a few, and I had to pick. Losing the equivalence relation I had in the first ten days (apart from NaN != NaN of course) was my pick.
Although I feel remembering where semicolons are needed is simple I still use them anyway for personal preference.
[1] This is a white lie - there is one additional vital ASI rule, but you already know it - don't put a linebreak after a return statement.
Except that there actually was a problem in this case as evidenced by the surrounding discussion. If the code had been written with a semicolon (or an if statement!) from the beginning, there never would have been a problem. You can argue that it wouldn't have been a problem if JSLint had been written "correctly" from the beginning, but every tool has bugs.
Code defensively, and don't get fancy unless you need to. You're not just complying with the language spec, you are communicating your intent to the other developers who will read and maintain your code.
Leaving out semicolons actually makes the intent more clear. People run into problems with semicolon insertion when they try to create an unnamed expression, i.e:
If you use a no-semicolon style, though, you must name all your expressions, which makes your intent more clear:> If you use a no-semicolon style, though, you must name all your expressions.
Thanks, I guess I missed that point.
I definitely agree that you should code defensively, but I'm just not seeing why adding semicolons where ASI kicks in is a practical thing to do. I don't agree semicolons make the code more readable, but if there was such a case you should definitely add them. And everyone - not just people who omit non-ASI'ed semicolons - will forget to include them where needed eventually, but that's not even an especially painful bug.
There are in my opinion more dangerous style choices that are ignored, like daisy-chained declarations. Writing
var x = 1,
y = 2,
z = 3
is a pretty lousy idea, as it unnecessarily couples the meaning of several lines of code together, and makes code harder to edit. If you unthinkingly truncate the first line you get
a pair of global assignments! That could be a really pernicious bug if you have supposedly 'local' variables sharing the name with an existing global object. It's better to be defensive and writevar x = 1 var y = 2 var z = 3
if only for editability.
My point is that the strong feeling on 'Semicolons' vs 'No Semicolons' is largely a product of focusing bias (http://en.wikipedia.org/wiki/Anchoring); to the extent that it is a problem either way it is a minor one, evidenced by the fact that similar stylistic dangers (see above) are either unfairly ignored or rightly not fretted over.
tl;dr I don't see a practically valuable reason to use them where ASI does the work for you, semicolon vs semicolon is barely a practical issue either way.
Maybe I am missing something. There are obviously cases where missing semicolons do cause problems. Is there a situation in which adding a semicolon where allowed causes problems or increases the chances of introducing a bug in the future?
I agree with the parent post. You can go wrong with either C-like or "NPM house" style. Linters are important, moreso in JS than in modern C and C++ (back to the future; I remember old C, which was wildly permissive).
Pick your poison, study it well and dose carefully.
1.) The non-use of semi-colons was perfectly valid JavaScript in that case.
2.) The parser portion of the minifier from Douglas Crockford did not account for this particular case.
3.) It became a flamewar because Douglas Crockford called it out as an error in a very ham fisted way, and the developer responded in kind. If a c++ compiler chokes on valid input, then a bug is sent to the compiler maintainer, and the developer rewrites their code until the bug is fixed.
4.) And that's exactly what happened. The minifier parser was fixed, and the Bootstrap code was updated to work around the parser error.
5.) The reason for not using semi-colons was legitimate. This is a library with a large audience, and not everyone has a minifier/compresser available. It was an easy win for something that wasn't needed. That's exactly the type of stuff that I want when I lean on 3rd parties.
So for me this could have gone a completely different way had the two parties been more civil:
So this is really more of a story about being professional to your fellow developers than anything that has to do with semi-colons or not.You can get by perfectly fine without semicolons. Going further, if you're using a linter like eslint, I think you can even configure it to give you a warning when the missing semicolon could cause problems.
THE RULES:
In general, \n ends a statement unless:
1. The statement has an unclosed paren, array literal, or object literal or ends in some other way that is not a valid way to end a statement. (For instance, ending with . or ,.)
2. The line is -- or ++ (in which case it will decrement/increment the next token.)
3. It is a for(), while(), do, if(), or else, and there is no {
4. The next line starts with [, (, +, *, /, -, ,, ., or some other binary operator that can only be found between two tokens in a single expression.
You have a junior dev now that's 2 steps behind because he's worrying about self-imposed "beatifying" edge cases instead of getting his code to run.
(For those who don't know the reference: [0], #3.)
[0] http://www.cs.yale.edu/homes/perlis-alan/quotes.html
It would be nice if it were deleted from the language entirely.
"use stricter"?
1. Modes are bad for implementations (this feeling was strong from V8 folks such as Erik Corry, but the new crew in Munich may not feel the same way), due to code branching burdens in testing and optimizing while keeping the complexity down.
2. Worse, what you propose would fork the language to double the testing burden for web devs (apart from engine implentors, item 1), since older browsers would ignore the useless expression statement.
This already happened with ES5 "use strict", and created real world bugs (e.g., https://bugzilla.mozilla.org/show_bug.cgi?id=631043, there are more like this).
Developers do not test all the combinations. Notice how a tower of M modes implemented via these string-expression-statement prologue directives can make a 2^M-fold testing burden.
http://www.2ality.com/2015/02/soundscript.html
How can real pragma syntax work on the Web? Only by using a compiler such as Traceur or Babel, otherwise old browsers would choke on the `use types;` unquoted directive.
Note how this can still complicate VMs, but it doesn't fork the test load for web developers. And if the type system results in early errors, plus better optimization with unforked runtime semantics, then the only burden on VM implementors is in the parser and type checker.
We shall see how this experiment works, but in no way is it like `"use stricter";`.
> We shall see how this experiment works, but in no way is it like `"use stricter";`.
I might be misunderstanding your comment, but stupidcar's link (http://www.2ality.com/2015/02/soundscript.html) mentions `"use stricter;"` and `"use stricter+types;"` as mode flags, although it also contains a big disclaimer ("The final version of SoundScript may look and work completely different").
Has the syntax been changed since February? If so, I'm interested in reading about it (but my Google-fu is failing); got a link?
http://www.mpi-sws.org/~rossberg/papers/JSExperimentalDirect...
There are two ideas:
1. A `"use stricter";` (originally "use sanity" :-/) prologue directive that would not change runtime semantics for code that passed all of static checks, (control flow dependent) dynamic exception-throwing tests, and runtime restrictions on object behavior.
2. A "SoundScript" mode, perhaps enabled by a `use types;` opt-in, or expressed some other way (per module?), that enables gradual type checking based on optional annotations inspired by TypeScript, but with TS-incompatible differences.
Mode 1 might fly with TC39, because it does not fork semantics for code that passes all checks. But for real-world code, there will be divergence when run on old vs. new browsers, so I expect push-back in TC39 on this mode being expressed by a useless string expression statement.
Mode 2 cannot be enabled by a fake-string-expression-statement "prologue directive".