We're going to commit a cardinal sin today and talk about syntax design! 0
There are three main alternatives for variable declaration:
The previous version of Vale used the first option, let and let mut. 1
However, it recently changed to a fourth option, which was so nice that it became the default behavior in version 0.2.
It's such a travesty to be talking about something as mundane as syntax during the week we're prototyping deterministic replayability!
Vale is a language that aims to be fast and memory-safe, while still being easy to learn. This syntax change helps with that!
Most languages can't just use x = 4, because that already meant something. That's the assignment statement, which modifies an existing variable that we already declared.
And alas, Python tried combining those two statements. It didn't go well. 2
However, there's another option here: let's change the assignment statement!
Instead of this:
We can have this:
In other words, x = 3 declares a variable, and set x = 42 assigns it.
It was odd at first, but after using it for a few weeks, we think this is a huge improvement.
Syntax design can be a tricky endeavor. When exploring new syntax, we had to suppress the knee-jerk unfamiliarity avoidance, and actually experiment. Without experimenting, it's easy to get stuck with what's familiar, even if there are better options.
This leads to problems; if you rename your x = 1 declaration to y = 1, but forget to modify x = 42 assignment below, you now accidentally have two variables!
To our great surprise, we've found that our codebases have a lot more declarations than assignments, so it makes sense to require the extra keyword on assignments because they're rarer.
We sampled three Vale projects. One had 111 declarations, and only 35 assignments. That's only 21% assignments! The other two were even lower, at 20% and 6%.
This isn't just Vale either. A randomly chosen Rust library, Rocket, had about 8%. 3
When did this happen? We used to assign variables all the time!
This is approximate; I used let(\s+mut)? (4437 results) and ^\s*[\w\[\]\.]+\s*[\+\-\*\/]?=\s+ (351 results) and which may have missed some corners, such as lambdas and mutating call returned values.
Parsing with regular expressions is fun!
We suspect moving towards more declarative patterns has contributed to this shift. Let's see some examples!
First, we no longer need assignment to return a value from an if-statement. Compare these two snippets in Scala:
Second, our for-loops became foreach loops, removing that pesky i++. Compare these two snippets in Java:
Third, we loop over collections less, and now use specialized methods like find a lot more. Compare these two snippets in Javascript:
Nevertheless, we seem to have a lot less assignments nowadays, so it makes sense to have the extra keyword on the rarer statement, not the more common one.
Note how the declaration doesn't specify whether we can change the variable, like let vs. let mut.
One of the benefits of that distinction was that we could easily know whether the variable could change in the future.
We actually kept that distinction for a while; we used the ! symbol, such as d! = 3.
However, we decided to not require it for local variables, because the set keyword makes assignment more noticeable than it was before.
For example, if we want to know whether the variable d = 3 can change, we just need to look for a set d = 7 keyword somewhere in the function, which is much more noticeable now than the previous assignment syntax was.
However, that reasoning doesn't apply to structs. A struct's members might be modified from various far-flung files in our codebase.
For that reason, we kept the ! on struct members. Other languages do this as well, such as OCaml, and it seems to be a pretty good balance.
Of course, we can't generalize too much. Every language is different, so we can't say that every new language should use this new scheme. Still, newer languages should give it some thought!
Thanks for visiting, and we hope you enjoyed this article!
In the coming weeks, we'll be writing more about our "region borrow checker" which helps eliminate Vale's memory safety overhead, so subscribe to our RSS feed, twitter, or the r/Vale subreddit, and come hang out in the Vale discord!
If you found this interesting, please consider sponsoring us:
With your help, we can write nonsense like this more often!
- Evan Ovadia
Vale aims to bring a new way of programming into the world that offers speed, safety, and ease of use.
The world needs something like this! Currently, most programming language work is in:
These are useful, but there is a vast field of possibilities in between, waiting to be explored!
Our aim is to explore that space, discover what it has to offer, and make speed and safety easier than ever before.
In this quest, we've discovered and implemented a lot of new techniques:
These techniques have also opened up some new emergent possibilities, which we hope to implement:
We also gain a lot of inspiration from other languages, and are finding new ways to combine their techniques:
...plus a lot more interesting ideas to explore!
The Vale programming language is a novel combination of ideas from the research world and original innovations. Our goal is to publish our techniques, even the ones that couldn't fit in Vale, so that the world as a whole can benefit from our work here, not just those who use Vale.
Our medium-term goals:
We aim to publish articles biweekly on all of these topics, and create and inspire the next generation of fast, safe, and easy programming languages.
If you want to support our work, please consider sponsoring us on GitHub!
With enough sponsorship, we can: