* For new features, people insist on LOUD explicit syntax.
* For established features [that turned out to be used disproportionately often], people want terse notation.
So, I argue, it's not really people getting used to the feature that allows it to be terser. It's that enough time passes that you figure out what features are used enough that they warrant the terse syntax (like the Rust example he gave).
It's a form of selection bias: there are many other established features that are rarely used and left with a verbose syntax but you don't notice them later because, well, they're rarely used.
You can introduce a notation that's terse without problem, so long as it's comprehensible when encoutered. Example: the (!Ā·!) operator (which I just made up), which can be placed around any expression to log the value of the expression to STDOUT. Its value is the same as the expression (so `(!3+7!)` equals `10`).
https://news.ycombinator.com/item?id=13192052 - 16 December 2016, 73 comments
Your students will be mad right away if you teach them the terse syntax, but mad later if you teach them the verbose syntax.
For new features, people insist on LOUD explicit syntax.
For established features, people want terse notation.
"The principle of least effort is another possible explanation: Zipf himself proposed that neither speakers nor hearers using a given language want to work any harder than necessary to reach understanding, and the process that results in approximately equal distribution of effort leads to the observed Zipf distribution.[5][31]" -- From: https://en.wikipedia.org/wiki/Zipf%27s_law
"Explicit syntax" would initially be less cognitive effort. Soon that effort would be gone and "terse notation" would lead to lesser typing effort.
Just finished two weeks of workshops and am exhausted, so this one will be light.
Logic for Programmers is on sale until the end of Chanukah! That's Jan 2nd if you're not Jewish. Get it for 40% off here.
I first encountered Stroustrup's Rule on this defunct webpage:
One of my favorite insights about syntax design appeared in a retrospective on C++1 by Bjarne Stroustrup:
- For new features, people insist on LOUD explicit syntax.
- For established features, people want terse notation.
The blogger gives the example of option types in Rust. Originally, the idea of using option types to store errors was new for programmers, so the syntax for passing an error was very explicit:
let file = match File::open("file.txt") { Ok(file) => file, Err(err) => { return err; } }
Once people were more familiar with it, Rust added the try! macro to reduce boilerplate, and finally the ? operator to streamline error handling further.
I see this as a special case of mental model development: when a feature is new to you, you don't have an internal mental model so need all of the explicit information you can get. Once you're familiar with it, explicit syntax is visual clutter and hinders how quickly you can parse out information.
(One example I like: which is more explicit, user_id or user_identifier? Which do experienced programmers prefer?)
What's interesting is that it's often the same people on both sides of the spectrum. Beginners need explicit syntax, and as they become experts, they prefer terse syntax.
The rule applies to the overall community, too. At the beginning of a language's life, everybody's a beginner. Over time the ratio of experts to beginners changes, and this leads to more focus on "expert-friendly" features, like terser syntax.
This can make it harder for beginners to learn the language. There was a lot of drama in Python over the "walrus" assignment operator:
# Without walrus val = dict.get(key) # `None` if key absent if val: print(val) # With walrus if val := dict.get(key): print(val)
Experts supported it because it made code more elegant, teachers and beginners opposed it because it made the language harder to learn. Explicit syntax vs terse notation.
Does this lead to languages bloating over time?
I find that when I teach language workshops I have to actively work against Stroustrup's Rule. The terse notation that easiest for me to read is bad for beginners, who need the explicit syntax that I find grating.
One good example is type invariants in TLA+. Say you have a set of workers, and each worker has a counter. Here's two ways to say that every worker's counter is a non-negative integer:
\* Bad \A w \in Workers: counter[w] >= 0 \* Good counter \in [Workers -> Nat]
The first way literally tests that for every worker, counter[w] is non-negative. The second way tests that the counter mapping as a whole is an element of the appropriate "function set"ā all functions between workers and natural numbers.
The function set approach is terser, more elegant, and preferred by TLA+ experts. But I teach the "bad" way because it makes more sense to beginners.