No Semicolons Needed
41 points by eBPF
41 points by eBPF
JavaScript seems to be the language that has given Automatic Semicolon Insertion a bad reputation.
ASI in JavaScript was a huge mistake. Of all languages, one that was designed from the start to be inserted into HTML and sent from computers that use Unix line endings to ones that use Windows line endings (and back then Mac line endings!) should never have had an automatic insertion rule!
Basically every other language should, yes, optimize the common case and omit semicolons on normal lines and have some kind of continuation character for long lines. JavaScript however needed an explicit delimiter for its intended environment and somehow didn't get it!
This is interesting that Haskell is not in the list. In my opinion, it has a similar rule to 'a different idea' the author mentions.
I used to hate semicolons. Then I started working in parser recovery for rustc. I now love semicolons.
Removing redundancy from syntax should be a non-goal, an anti-goal even. The more redundancy there is, the higher the likelihood of making a mistake while writing, but the higher the ability for humans and machines to understand the developer's intent unambiguously.
Having "flagposts" in the code let's people skim code ("I'm only looking at every pub fn") and the parser have a fighting chance of recovering ("found a parse error inside of a function def, consume everything until the first unmatched } which would correspond to the fn body start and mark the whole body as having failed parsing, let the rest of the compiler run"). Semicolons allow for that kind of recovery. And the same logic that you would use for automatic semicolon insertion can be used to tell the user where they forgot a semicolon. That way you get the ergonomics of writting code in a slightly less principled way while still being able to read principled code after you're done.
This is a nice list. Lua is my favorite among those mentioned.
Haskell and OCaml are two languages that would make good entries. They both deal with whitespace pretty well despite having distinct approaches.
Haskell is whitespace sensitive, but the programmer can add braces and semi-colons to "override" the newline rules.
OCaml code often looks like it's whitespace sensitive, but really it isn't. Just very intentionally designed grammar, much like Lua.
Haskell is whitespace sensitive, but the programmer can add braces and semi-colons to "override" the newline rules.
That’s not really correct. Haskell is defined in terms of braces and semicolons, then has a notion of “layout” layered over it, which translates white space forms into those.
Back when I was first learning Haskell, the distinction that you mentioned was my biggest stumbling block. I knew that the language was white space sensitive, but I was never sure where and when I needed to add that white space. The most common guidance I received at the time was
Add a level of indentation whenever you would use curly braces
Great, so when do I use curly braces?
Curly braces aren't idiomatic. Use indentation instead
🤦
I'm now far enough away from beginner materials that I don't know if they've improved the answers there, but I stumbled far harder over the white space than I did over the monads.
In all my language experiments I came back to just making mandatory semicolons. It resolves a lot of complexity in the parser. Particularly now that agents are writing code anyways, that little saving does not give you much for the extra pain it causes everywhere.
One thing I've found is that OCaml/Rust style implicit returns, where you can just end a sequence of statements with foo instead of return foo, sadly does make this a lot harder. Lua makes its life a lot easier by having each statement only start with a keyword, assignment, or function call, and it'd get more squirrely if you could just have -3 dangling out on its own at the end of a function body. Though as the article says, it's not perfect.
My language just shrugs and makes newlines syntactically equivalent to ;, and has plenty of places in the grammar where it just eats zero or more delimiters until it hits something more interesting. This is effectively the inverse of the Go approach, instead of "semicolon insertion" you do "newline removal". I messed around with Go-style rules but it kinda came out the same. It was easier for me to modify my parser than my lexer, so, eh.
I personally categorize languages by how many delimiters are needed:
| Lang | # of Delim | Example | Explanation |
|---|---|---|---|
| APL | 0 | 6=+/[1 2 3] |
End of char separates it from the next char |
| Forth | 1 | 1 2 3 + + 6 = |
Space |
| Lisp | 3 | (= (+ 1 2 3) 6) |
(, ), space |
Lisp could also be seen as fundamentally 2 like ((+)(1)(2)(3)).
Lisps avoid this kind of problems entirely, due to S-expressions (and indeed, usual Lisps use semicolons for denoting comments instead). The equivalent discussion in Lisp land would be about other syntaxes for Lisps, among which I find Wisp the most pleasing. What new things are happening in the space of Lisp syntaxes?
Semicolons in Odin are optional, and still have some valid uses. Author didn’t mentioned that, but you can use them to e.g. inline defer right after allocation: foo : ^Foo = new(Foo); defer free(foo), and also recall putting semicolon before else in some cases.