As you might expect from someone who tried her hand designing a programming language, I have a lot of strong opinions about language design. Recently I’ve been thinking about doing some hobby programming, but lately I haven’t found any languages to my taste. Maybe I’m just too picky?
Probably my most subjective requirement of a language, but also the one I feel most strongly about: ahead-of-time typechecking. Types eliminate so many classes of annoying mistakes and serve as a strong communicative tool to other developers. I have picked apart one too many tangled Ruby-on-Rails apps or fragile Javascript frontends to think dynamic types work for me. I know some folks feel strongly the other way: good for them, but not so much for me. From here we still have plenty of options, too many to even bother listing. The world is full of languages with static types.
Not satisfied with merely static types, though, I want sum types (sometimes called enums, variants, unions, etc) and null safety (pointers aren’t nullable by default). It’s nice to be able to encode invariants directly into our typechecker rather than having to defensively maintain them throughout the body of the code, and I find sum types crucial for that sort of encoding. Our language options are already beginning to dwindle: we have Rust, Zig, Gleam, Typescript, and a host of ML-inspired languages like OCaml and Haskell. Untagged unions, which introduce more correctness problems than they solve, don’t count (sorry C and C++).
Our last table-stakes, I-want-it-for-any-program-I-write property is memory safety. Even without getting into interesting safety properties like “mutable aliases are forbidden”, temporal (e.g. no use-after-free) and spatial (e.g. no buffer overflow) memory safety buy us a lot. If I never have to open up valgrind again it will be too soon. Sorry Zig, happy trails.
Fortunately our language pool is still big enough to write most kinds of software: Rust, Typescript, Haskell, OCaml, Gleam, etc. But the real problem is that I like to write video games, and gamedev shape my opinions in somewhat idiosyncratic ways.
The first game-inspired requirement: I really want stack allocations. Whenever I have to heap-allocate something as simple as a pair of floats to represent coordinates, I feel physically gross. My formative programming experiences were marred by frustration with my poorly-optimized code written for bloated runtimes struggling with sluggish hardware. Obviously I want to avoid GC pressure or a slow malloc in the hot path of a frame, but I also want to avoid the overhead of chasing pointers for data structures as simple as “an array of two-dimensional vectors.” Stack types are the only way I’ll be satisfied. Sadly we must now part with Typescript, Gleam, Haskell, and OCaml. Well, we do have Rust… and I guess we can keep OCaml in the form of OxCaml (for now).
The second game-inspired requirement: it’s gotta run on Windows and macOS. Not only do I want to target Windows, but when I’m sitting at my desk to write code it’s at my rarely-used gaming PC. And I when I’m not at my desk, I’m usually writing code on my M1 Macbook I got to keep after a layoff. For most languages this isn’t a problem, but we must now eliminate OxCaml. Easy come, easy go.
We only have one real contender left, and I am a known Rust fangirl. But I’ve never quite been satisfied with how it measures up in the last thing I want in a gamedev language: compile times. For iteration, there’s nothing more important than making tweaks and seeing how they feel in play. The two ways to cope with slow compile times tend to be somewhat-fragile hot reloading (see my own efforts), or introducing a scripting language (which will inevitably violate one of our earlier requirements). Neither of those options make me particularly happy.
There are lots of options that meet all-but-one of my asks, but none (that I know of) which meet all of them. I often think “if only”:
- if only Rust compiled quickly…
- or if only Go had sum types, and didn’t have nil pointers…
- or if only Zig was memory-safe…
- or if only C# had sum types…
- or if only Typescript or Haskell had stack allocation…
- or if only OxCaml supported windows…
…then I could be finally happy. Actually—if I’m honest—then I could find new things to complain about.
Postscript: Does anyone else care?
I don’t want to give the impression that I am a PL critic par excellence, and everyone else merely muddles along without a clue of their own poor taste. For example, sum types and non-nullable pointers have been staples of functional programming for decades, but they have only recently begun to filter out to the wider programming language sphere. While stack allocation isn’t as widespread in GC’ed languages, it’s also “only” important for performance.
Off the top of my head:
- I know the Rust project knows that compile times are bad, and people are constantly chipping away at them. They’re way faster than they used to be, but still not what I’d like in my heart.
- I think C# is literally getting union types this year. Maybe I’ll have to dive back in to my fairly dismal C# nvim setup and find if anything has improved.
- They’ll add stack allocation to JavaScript runtimes well after pigs fly. And I’m not sure Haskell or OCaml will meet these requirements much sooner.
- I’m not going to hold my breath for Go or Zig to change tack (the former on nil pointers and sum types, the latter on memory safety). These are fundamental value differences between me and the people who make decisions for those languages. It is frustrating, though, because slightly different taste on their part and I could have an alternative to Rust with much better compile times. Ah well.