Thoughts on Standard ML as of 2023

Today marks one week since I started writing some Standard ML1 and I'd like to share some thoughts I've had during my journey. I should begin by addressing the standard (🥁) question people ask: "Standard ML, really? Why?!". Well, there are a couple of reasons for that.

Why?

Standardized

This is a double-edged sword, indeed. While offering a lot of stability over the whole ecosystem, it also slows down progress2 a bit (my opinion). Now, while I write my code, this has been a blessing. You have 40 years of code to run and you know that most of it will Just Work ™️.

Type System

Although not being a Type System aficionado myself, I'm really enjoying my $CURRENT_JOB writing F#. It has the best type system I have had the pleasure to work with. Standard ML has an even better type system and a better scenario regarding type inference (less typing!). This is noteworthy considering my primary experience lies in Python, Go, and Clipper. I believe this comparison alone speaks volumes.

Small ecosystem

Look at it yourself on some of the biggest forges: GitHub and sourcehut. Considering your goal, this might be a showstopper. However, I really like writing libraries and tooling in general, making this a huge plus.

Small language

This is topic is highly subjective. I mean, to the point of starting a war. Still, I personally consider Standard ML to be small and concise enough. When working with it, the syntax does not "slip your mind" and while possible, most people don't implement a domain-specific language based on custom operators. It reminds me of Go in the sense that reading code is straightforward, and writing it doesn't demand extensive symbol recollection (it does trade terseness for verbosity).

From zero to action

Note that some of this might be easier or harder if you are not a Nix user.

How does it look like to start writing Standard ML nowadays? While you don't really have powerful IDEs and a multitude of tools such as linters and debuggers, you can still go really far.

Community

There's the helpful #sml channel on Libera Chat, Project Savanna's Discord server (mostly focused on tooling) and there's also the r/sml subreddit.

Editor support

You can use the amazing language server for Standard ML called Millet. It works just fine with Emacs' eglot and Millet also provides a Visual Studio Code extension that works without requiring any configuration.

Compilers and build systems

While installing a compiler is as easy as adding a new line on my devShell (see below), working with multiple compilers can be tricky. The most known, being MLton and SML/NJ, use different build systems. The former using MLBasis (.mlb) and the latter using Compilation Manager (.cm). For the time being, my solution has been to support both build systems, each on a separated file.

mkShell {
  buildInputs = with pkgs; [
    mlton smlnj
  ];
}

What I like and what I don't

It is old and it shows

Can you imagine working without proper UTF-8 or Unicode support? You better prepare yourself to that surprise. There's some discussion around this on Successor ML. Also, I'm not sure why, but makestring was removed from the standard. This leaves you without a polymorphic "print" function3, it's quite hard to debug programs without this.

Type inference

Coming from F#, I used to think its type inference was good... Boy, was I wrong! Do I miss SML's type inference when writing F#. It's really hard to explain the experience to someone that never had contact with this kind of type inference.

Compilers

The most known compilers are awesome (MLton and SML/NJ), they are quick to compile code and compile to fast native binaries. Honorable mention to SOSML, that allows you to write Standard ML on the browser.

Package manager

There's a great question on Stack Exchange about ML-like modules and package managers and you should probably read it. There are two main package managers: Smackage (which seems to be more of an installer than what I'm referring to) and smlpkg. While I haven't used none of them, there's nothing like cargo or npm with a public registry and standardized manifest.

  1. I intend to write more about the challenges I faced with this project.

  2. Check Successor ML to see more.

  3. Poly/ML exposes a function that does just that: PolyML.print. The problem now being tied to a single compiler...