DEV Community

Joe Clay
Joe Clay

Posted on • Edited on

How do you feel about braces and semicolons?

I'm (slowly) working my way through Crafting Interpreters, and trying to put together a design for my little interpreted language. Thinking about the syntax has got me curious - do you prefer for a language to have:

  1. Braces and semicolons (C, JavaScript, Rust)
  2. Braces and no semicolons (Go, Wren)
  3. Semicolons and no braces (I'm not actually sure anyone does this?)
  4. No braces and no semicolons, with significant whitespace (Python)
  5. No braces and no semicolons, with no significant whitespace (Lua)
  6. Some other wacky design I've not thought of...

Personally, I tend to prefer the explicitness of option 1, but at the same time I don't really like that {} has a double meaning (is it a scope, or is it an object literal?). How about everyone else?

Top comments (41)

Collapse
 
dmfay profile image
Dian Fay

Braces defining scopes and braces defining objects have different effects, but I'd question whether the meanings of the braces themselves are that distinct. A scope encloses process in order to execute it conditionally, iteratively, later, or in particular contexts; an object literal encloses information so it can be referenced, destructured, and otherwise operated on. Object literals can also execute code intra-declaration, blurring the semiotic lines further:

const obj = {
  one: whateverThisReturns(),
  two: three ? four : five
};

The significance of braces isn't an either-or of scope vs declaration. It's that the contents -- whatever those may be -- represent an interruption in the top-to-bottom flow of execution. Enclosure is enclosure, no matter what you're doing inside it.

Personally, I like braces and you can take my semicolons from my cold dead hands.

Collapse
 
17cupsofcoffee profile image
Joe Clay

Aah, that's an interesting way of thinking about it that I hadn't considered!

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

Genuinely curious, but why do you like semicolons? You seem to use a style of code which can be interpreted without them, yet you still want them.

Collapse
 
dmfay profile image
Dian Fay

I've been writing JavaScript more than anything else for the past five years but my background is all Java and C# and old habits die hard.

Also, JS being explicit about statement terminators would have saved me more time than I'd like to admit trying to figure out what was going on with

return
  someComplexConstruction;
Thread Thread
 
mortoray profile image
edA‑qa mort‑ora‑y

Without the fear of JS wackiness, would you be comfortable with semi-colon-less code?

I grew up on semi-colons as well. I don't use them in JS either now -- I'd rather play with fire than see them anymore. :)

Thread Thread
 
dmfay profile image
Dian Fay

It'd depend. In C-family languages, I wouldn't, at least initially. But I could probably get used to it. I still don't like Groovy but there's a lot more going on there than the lack of semicolons.

Other languages, anything goes.

Collapse
 
ben profile image
Ben Halpern • Edited

No braces and semicolons (I'm not actually sure anyone does this?)

Ruby has braces but are usually reserved for single-line declarations. This is the common block syntax.

@people.each do |person|
  person.say("hello")
end

if something
  do("something")
elsif something_else
  do("something else")
else
  do("nothing")
end

Ruby looks like Python but does not use significant whitespace. It's definitely an interesting case study for language syntax design.

Collapse
 
17cupsofcoffee profile image
Joe Clay

Ah, slightly confusing wording on my part - I meant 'no braces, yes semicolons' for that option :) Will rephrase that.

Either way, I really like Ruby's syntax! Lua has a similar thing going for it where it looks like a Python-ish language, but its grammar doesn't care about whitespace/line breaks.

Collapse
 
rhymes profile image
rhymes

I also have the feeling that using a delimiter might be easier for designing a new language, might be wrong though.

Definitely no semicolons :P

Collapse
 
fnh profile image
Fabian Holzer

Some other wacky design I've not thought of...

Lisp-style S-Expressions, make parsing very easy.

In Erlang a function body starts after a an arrow, ends with a colon and
expressions are seperated by commas.

I profoundly dislike the Python's paternalism regarding significant whitespace. On the other hand, in Haskell you have it as syntactic sugar for braces and semicolons, with which I can live very well.

Wirth-style languages use keywords (begin, end) for denoting blocks, which I find to be a bit verbose.

Collapse
 
17cupsofcoffee profile image
Joe Clay

Ah yeah, totally forgot about Lisps. I really like them in theory (especially from a parsing standpoint), but in practice I don't tend to enjoy writing them that much ):

Not hugely familiar with Erlang beyond writing a bit of Elixir - will look into that further!

And yeah, can't say I'm a big fan of significant whitespace myself.

Collapse
 
numberjay profile image
numberjay • Edited

it's unfortunate that all main lisps (common lisp, scheme, racket, clojure, ...) needlessly use an excessive amount of parenthesis

it gives the impression that lisp is inherently a 'syntactically verbose' language, which is totally false

take scheme:
(let ((a 1) (b 2)) (f a b))

now take a 'syntactically sane' lisp:
(let a 1 b 2 (f a b))

s-expressions were originally conceived as an internal representation for programs to be parsed by computers, not by humans, and so justifying the verbosity

but then programmers started to find s-expressions extremely convenient to use (expecially because they trivially allowed metaprogramming by treating code as data) that no one cared to build the human readable m-expression syntax that lisp were originally supposed to have

that was a fortunate accident, but the excessive parens sticked instead of being reduced to the absolute minimum which would bring lisp to a better place than most currently used languages in which parenthesis, brackets, braces and punctuation abound

Thread Thread
 
17cupsofcoffee profile image
Joe Clay

I totally agree, and I think this is why Clojure is one of the only lisp-y languages I've sunk any time into. A little bit of syntax sugar goes a long way!

I also find the design of Julia very interesting - they have a Python-like syntax, but parse it into s-expressions. This gives them some of the benefits of a lisp (easy meta-programming, code-as-data, etc) without being quite as painful to write. I think Elixir does something similar, too.

Collapse
 
tbroyer profile image
Thomas Broyer

No-semicolons are a problem IFF expressions can be statements (not sure about the terminology here).

For example

foo = bar
  - baz

could be read as either

foo = bar - baz

or

foo = bar;
-baz;

but -baz could be forbidden as a statement (Go rejects it, but neither Kotlin or JavaScript do).

No semicolons also means you sometimes have to end your lines with \ to have them parsed correctly, or make sure you put binary operators at EOL (see above, also applies to . or ->, or logical operators).

Also, braces, definitely! Less typing, and lighter-weight visually than end keywords (or similar, please don't make context-dependent keywords like in shell: if …; then …; fi, while …; do …; done, case … in … ;; esac, etc.), and significant whitespace, well… (nothing against Python, but there must be reasons other mainstream languages didn't copy this facet of it)

Collapse
 
17cupsofcoffee profile image
Joe Clay • Edited

Yeah, that's definitely a problem with parsing languages without semicolons - I quite like the solution that the developer of Wren (who, incidentally, is also the author of Crafting Interpreters) went for:

Newlines (\n) are meaningful in Wren. They are used to separate statements:

// Two statements:
System.print("hi") // Newline.
System.print("bye")

Sometimes, though, a statement doesn’t fit on a single line and jamming a newline in the middle would trip it up. To handle that, Wren has a very simple rule: It ignores a newline following any token that can’t end a statement.

System.print( // Newline here is ignored.
    "hi")

I find that a bit easier to keep in my head than JavaScript/Go's automatic semicolon insertion rules.

Collapse
 
tbroyer profile image
Thomas Broyer

AFAICT, this is basically what Go does too (golang.org/ref/spec#Semicolons, expressed as a whitelist rather than blacklist) except -baz by itself, while a valid expression, is not a valid statement, so code wouldn't compile rather than most probably creating a bug.

Of course, it's not actually equivalent, but the rule is easy. By this rule, the following wouldn't compile, but would be accepted in Wren (Go doesn't have ternary operator either though)

foo(bar
    ? baz : qux)

I suppose the choice also depends whether you want a "lenient" language (like JS) or a stricter one (like Go). It's hard to create subtle bugs in Go due to parsing alone, it's way too easy in JS, which is why people add ESLint or similar to tell them when they likely did it (though it probably cannot detect everything).

Automatic formatting (Prettier) also helps detecting such bugs ("why did it un-indent that -bar?"), but that's still left to the developer to "decide" whether he introduced a bug or not.

I, for one, prefer strictness over "format your code the way you want it". Python is good in this respect if significant whitespace is your thing; otherwise, lean towards Go rather than JS (when it comes to semicolons)

Collapse
 
cad97 profile image
Christopher Durham

The way Swift handles this is interesting.

let x = 10
- 1
// is
let x = 10
- 1;

let x = 10
-1
// is
let x = 10;
-1;

let x = 10 - 1
// is
let x = 10 - 1;

let x = 10 -1
// is
let x = 10; -1;
// Error two statements on the same line must be separated by a semicolon

The rule is basically [space][op][space] or [nospace][op][nospace] is an infix operator, [space][op][nospace] is a prefix operator, and [nospace][op][space] is a postfix operator.

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

In Leaf I went for braces without semicolons. I dislike giving whitespace significant meaning (as in Python) as it can be hard to debug, and may not support the desired code structure. I also prefer having an explicit end notation on blocks.

There is also no ambiguity with {} blocks in Leaf, they are always code blocks. Tuples (objects) are denote with [] blocks. I'm debating whether the commas in tuples should also be optional, as it's common to list the items on multiple lines.

Collapse
 
msoedov profile image
Alex Miasoiedov • Edited

Both braces and semicolons were used to simplify language parser. In most of the time they have no value for code readability. However I would agree that in some rare cases optional brackets could improve readability of a complex code structure.

I believe the next generation programming lang will get rid of under_score or cameCase style to look more like a natural human language


def find public ip(self):  # instead of def find_public_ip(self):
      ....
Collapse
 
17cupsofcoffee profile image
Joe Clay

Natural-language style programming is definitely interesting - I think the coolest example I've seen of it is Inform 7, a language used for writing text adventures which is designed to look like normal (if somewhat stilted) English prose:

"Hello World" by "I.F. Author"

The world is a room.

When play begins, say "Hello, world."

Whether you could (or should) create a general-purpose programming language in that style, I'm not sure - it's interesting to think about, though :)

Collapse
 
math2001 profile image
Mathieu PATUREL

I really don't understand, from a user point of view, why a programming language should have braces and semi colons. We can do without them very well (Python is the living proof).

Pros

  • looks less "crouded"
  • less typing
  • lighter files (with js especially)

Cons

  • tend to make typo (oh, i forgot an other f*** semi colon on line 239. Go back, add a semi colon, and reload).

For bracket, you indent your code anyway, no? What's wrong with not having an explicit separator?

Something funny I saw a year ago I think in a screencast, from a bracket loving guy.

def some_function(): # {
    some = code()
# }

I think argumentation with that guy would have been a nice waste of time :D

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

Significant whitespace (indentation defining blocks) can cause a lot of unintended trouble. Minor formatting errors, which can arise during merges, can completely change the meaning of the code. Unintended editor setting variations (also common) can also change the meaning of the code.

Similar whitespace can also come from different unicode characters, resulting in code that looks visually correct, but is wrong. Copy-and-paste from different sources can give rise to such a problem.

The lack of closing notation can also lead to a visual anomoly of dangling code. It can also be unclear in nesting which former block is continued. Closing markers can help identify the ends of blocks -- it's visually more distinct than unindenting.

Relying on whitespaces for blocks can also complicate situations where inline objects or closures are neede.

Collapse
 
briankephart profile image
Brian Kephart

My initial reaction to JS was negative in part because of lines like this:

});

I hate that. Especially seeing consecutive lines containing nothing but braces and semicolons. Ugly and hard to read IMO.

Collapse
 
andreanidouglas profile image
Douglas R Andreani

I'm very fond of the C programming style, maybe because I started with it, although python code generally looks very elegant as well.

Personally, I keep the old (but awesome) K&R indent style due to the considerations for the Linux kernel development

Collapse
 
nektro profile image
Meghan (she/her)

braces and semicolons?

Both are highly required