Why semicolons?

To be honest, semicolons also help the readability of the code; they preserve the "List of instructions" feeling of the programming language; they work just like the semicolons in this sentence; they separate ideas while in the same context while if I didn't use semicolons (or much punctuation) then I'd end up with rather complicated ideas. In this case I am going to use no punctuation between words and leave the reader to decided when an idea ends like when for example I'm using a multi-line statement It's up to the compiler to decide when I finish a sentence conclusively given how difficult the past few sentences (while missing their "."s) are difficult to interpret it just improves the readability of the code and in this case my English and helps the reader understand the code/English at a higher level
.. Jeez that was painful to type. Sorry if my ideas weren't very easy to understand; there was a lack of information for you to properly parse make sense of it.


Oh and also, there are soooo many languages and developers that are used to the ; rule that it's like trying to advertise that just because there isn't a need to use pens; as pencils work just fine; that there is no need to produce or support pens anywhere -- its preposterous!

15 Likes

I was discussing this with a coworker recently and found this article, which has a nice discussion of semicolons in rust:

The tl;dr is semicolons are needed in a language that has:

  1. Pervasive expressions (rarely statements)
  2. Static typing with type inference and good compiler errors
  3. Implicit return allowing for ergonomic closures

If there's a language that has all those without semicolons, I'm curious what it is.

7 Likes

@baumanj

I mean the trivial answer to all three of those is Haskell, but I'm guessing that's not the language you're looking for?

Haskell has implicit returns, fully static typing with good errors when typing doesn't match (good errors debateable, but I find that they make sense at least when you understand the type system), and is 90% expressions. (things in do blocks are arguably statements).

I think if you added "uses both expressions and statements pervasively" to the list, then you wouldn't have any (popular) languages. Even without that, haskell's errors being good are debateable.

1 Like

I'd like to stress the significance of semicolons with implicit return. A common error in languages with implicit return is to change what gets returned when adding a line to a function. The error happens even with strongly typed languages if you happen to add a line that evaluates to the correct type. While I've added lines to Rust methods, I've never had a problem. The program won't compile if I don't add a semicolon to the former final line. That forces me to look at what I just did.

4 Likes

I don’t know Haskell. How does it handle the implicit return issues described in the article?

In haskell you rarely have a sequence of operations, a function superrarely is more than a single expression.

And since in a function there can be no side-effects (unless explicitely declared) you usually code in a complete different way than you'd do in rust.

Even though I have not read that article, but the problems mentioned in that article probably do not exist in haskell, because the particular feature doesn't exist in haskell…

Haskell does not have return or statements, only expressions.

Well, okay, it has do-notation, which resembles statements if you don't squint too hard. But Haskell has very few syntactic elements, so there is no need to worry about disambiguating between e.g. a; [b].c and a[b].c like there is in Rust.

(It also has a function called return, which you might see used in places that occasionally resemble a function return, but it doesn't really do what you might think)

7 Likes

In short, all values are immutable and all functions are pure in Haskell, with the one exception of I/O monads.

Because everything's immutable (and lazily computed), as others have said, statements don't need to exist. Since statements don't return a value, they would do nothing in Haskell. Thus everything is an expression.

For example, to write a function adding two numbers, I just write

my_sum a b = a + b

I/O is a slightly different beast, and does have something like statements (whether they are or not is a whole different topic). But even with do, the last IO statement can still be implicitly returned:

getName = do
    putStrLn "What is your first name? "
    putStr "> "
    getLine

main = do
    name <- getName
    putStrLn ("Hello, " ++ name)

Note that in this, all types are inferred and statically checked.

1 Like

Rust already has this burden of special chars due to types and lifetimes. When I show Rust code to friends they're telling me it looks like Perl :frowning: I believe removing of all unnecessary chars would be beneficial for Rust especially as there is already too much essential once.

It's also relevant for curly brackets. But it would probably make auto-formatting harder more error-prone.

3 Likes

In my experience, about 50% of people who have ever tried Python can't seem to get over the "weirdness" of semantic whitespace.

Those "many modern programming languages" that don't require statement terminators or separators are mostly small, and the few big ones are still getting on their feet. Really, of the major general-purpose languages, it's just Python. Go and Swift are coming along, and may eventually supplant the likes of Java and C++, but we're just not there yet, in terms of market share, or industry penetration, or what gets taught in Computer Science 101. For a large number of programmers, probably a majority, curly braces + semicolons are "normal" and semantic whitespace is "weird".

I think Rust took a good direction. There are plenty of arguments in favor of statement separators or terminators, as other people in this thread have already mentioned, but they're also familiar. A language only has so much strangeness budget. Speaking for myself, one reason I've never gotten into Haskell is that the syntax is so unusual that I find it hard to really care about the big ideas behind it. I first came to Rust because it was a "safer C"; the fact that Rust code reads, on the whole, quite similarly to C code helped draw me in, because I could easily understand how the compiler was checking and translating it.

I probably still would have come around to Rust if it had optional semicolons à la Go. But other people might have been turned off and confused by it, just like my coworkers who are turned off and confused by Python's semantic indentation. There's no value in picking an unusual syntax just because that's the new and hip thing to do (and, after all, in ~2010 it probably wasn't).

Another fun fact about Python: the : in a for, if, try, while, with, etc. is never necessary, grammatically; the language would be fine without it. The designers of ABC, a strong influence on Python, discovered through user testing that people who had not programmed before found it easier to understand code when the colon was present. So if your argument is "it's unnecessary and therefore should be removed," perhaps think again: even unnecessary things can serve a purpose. (reference 1, reference 2)

3 Likes

[Moderator note: Please focus on adding new information, and not back-and-forth arguing.]

1 Like

Haskell doesn't have semicolons to separate statements because it doesn't have statements. F# would be a better example.

2 Likes

Let's not make this discussion about "I am used to X, so I don't like the fact there is no X". I, for instance, like both Python's and C's styles (except for braceless then/else bodies in C. Those were a mistake). Let's think about usability.

The key for the discussion is to understand that two different entities will parse the code written: the compiler/interpreter, and humans. It turns out that the most efficient disambiguator for any kind of sequence is just a single token separating each item. In most languages, this token is a coma or a semicolon. Machine parsers are good with that. But humans are not. With multiple levels nested at different levels, raw tokens do not suffice for humans to parse it. We are far better are parsing using whitespace. Hence the "coding style" rules for when the language does not enforce whitespace.

A language such as Python decided, since whitespace ends up being needed by humans anyways, to make that parsing requirement apply to the machines as well. It is a very honest reasoning imho.

But such a choice has its drawbacks. Not only does it remove freedom from the programmer to lay out their code as they see fit (e.g. short inline blocks), it also makes it complicated in an expression based language such a Rust.

Example

Imagine the following code:

match f() {
  // ...
}

Now, imagine you want to debug the value returned by f(), and that the dbg! is inexistent or unheard of. You can replicate the macro's behavior with something like:

match { let x = f(); eprintln!("{:?}", x); x } {
  // ...
}

Having the option to inline that kind of blocks make the need for semicolons worthwhile imho ; but that is just it: an opinion, a matter of personal preference. It is impossible to choose something that pleases everyone, but it is up for everyone to be pleased with, or at least to tolerate, a chosen consensus.

4 Likes

Pyret is a programming language for teaching without semicolons. It prefers expressions and implicit returns but has statements and imperative control flow too.

truly, lifetimes are real rustician's beasts on advanced levels... much more like ****h in C-lang yet
i dislike things like &&a

The first customer of semicolons is the parser. Parsers are pretty dumb: they need quite a bit of structure to their token string to be able to untangle it into a parse tree. Syntactic ambiguity becomes a real problem: even if the parser manages to find a parse tree for your program, it is likely to be the "wrong" parse tree — one that has a completely different meaning than what you intended.

Both Python and Haskell use newlines to provide "invisible semicolons" to the compiler. LISP/Scheme is maybe a better example of a language without semicolons: turns out bracket matching is sufficient if you force bracketing of everything.

Smalltalk mostly uses period as a statement terminator. I strongly prefer this, but the compiler's job is made much harder since a dot has so many other overloaded meanings.

In general, though, semicolon in most modern programming languages serves the same role that period serves in English; it terminates or separates the syntax for a single "idea" / statement. That lets the parser figure out where the statements are; it also aids readability of code, since we were trained as kids to look for punctuation like this when reading.

Languages without punctuation are generally hard for humans to read and understand; they also tend to have simple syntactic structure so that compilers can cope with parsing. APL is hard to read for so many reasons, but the lack of any separator syntax is one that is often overlooked. Also, APL has an "everything is an operator" structure that is parseable in this setting, so the compiler can cope.

Pascal uses semicolon as a statement separator, but an optional null statement can be put at the end of a sequence of statements, which makes it usable as a terminator. The whole separator / terminator religious war is a whole 'nother thing: I'm not going there.

So…yeah. We're probably in with the semicolons in computer programming for the long haul.

7 Likes

I think this is what the OP was referring to.

2 Likes

Note that Haskell also has a brace notation that does require semicolons and is sometimes useful.

Maybe another fact here is, that some big tech companies do forbid dropping semicolons in languages that do not require them. Examples are the AirBnB and Google JavaScript code styles. They felt, that allowing people not to use semicolons was actually not worth the trouble and therefore require semicolons in their code, even if they are not strictly necessary.

From personal experience, I think semicolons make reading code just way easier since you once an expression is finished. Especially when looking at multiline statements.

2 Likes

That's different. It's not that JS doesn't require semicolons; it's that it automatically inserts them, sometimes changing the meaning of code in the process.

That terrible design decision built into the language makes automatic semicolon insertion everyone's problem. If you come into JavaScript from languages with semicolons and start writing code with semicolons like you're used to writing, you'll no doubt eventually get bitten by this:

// This doesn't do what you'd expect
function foo(y) {
    let x = 1;
    return
        (x + 1) * y;
}

The only sensible way to write JS therefore is to memorize the rules for automatic semicolon insertion, at which point explicit semicolons at the end of lines are nothing more than garnish.


Edit:

I misread due to the double negative. They require semicolons? I find that surprising.

Edit 2:

I see. They recommend semicolons so that a linter can catch code that is broken by automatic insertion. That's... brilliant, actually.

2 Likes