by seertaak | 2026-02-22 08:17:38+00:00
First, let's distinguish between two types of syntactic constructs: null and left denominations. (Terminology borrowed from Pratt parsers.) Null denominations can exist on their own, left denominations can't -- they are inherently chained (eg arithmetic expressions, statements in a block, or elements of a tuple), and allow a succinct, infix notation for variable-length constructs (no lispy parentheses hell).

Second, null denominations usually introduce names -- whether for variables, types, functions, lifetimes, macros, etc. One exception to this are value expressions ("hello", 8, 10+10 are examples), which either appear on the RHS of variable/const declarations, as actual parameters in function invocation, create "no-op" expressions when appearing on their own (a bit weird) or -- when they're the last expression in a block -- sometimes indicate the value returned by the block. The other exception would be directive type constructs - eg directives to import names from another module, directives to give hints to the compiler, etc.

The golden rule of good language design, as I see it, is this: null denominations must begin with a fixed and unique token. The only permissible exception should be for value expressions; an exception which exists because value-expressions are so common that requiring a prefix -- like Tcl's 'calc' -- would be insufferable.

Julia breaks this rule for global variables. (Fair enough, Python also commits this error, but it's a mistake and a source of bugs!) But wait, there are also "const" bindings and "local" bindings -- where it gets it right, but now your syntax isn't consistent. So now you need to keep in your head these differences -- and know the difference between a soft and hard scope -- when you want to write a function which modifies a function using macrology.

As a point of taste on the choice of prefix token: introduction of variables through "local" is just as weird as C++'s "auto". (And at least Bjarne Stroustroup had an excuse for that choice.) Anyone who introduces a global variable in a local scope should be punished imho, so there's no need to say "this is a local variable", it's obvious from the fact that the name is introduced inside a function. Instead, my personal preference is to introduce constants through "let", and variables through "var". The former is well-known to anyone numerate, and the latter is ubiquitous in software engineering. Both read well; they're as close as possible as you can get to constructs in English.

Depressingly, Julia makes the same mistake for functions. I get that it wants to appeal to Mathematica users, but Python already proved you don't need to do that. This is a programming language; brainy types, like mathematicians and physicists, aren't going to be flummoxed by an unfamiliar notation for function definition.

I mention macrology; it's not just that. Let's say you want write a syntax highlighter -- you need to take into account all that weirdness. If null denominations have a fixed & unique prefix, parsing is easy-peasy.

This is not the only thing that's weird, I'm not a huge fan of the use of '=' within for-expressions, but that's just taste.