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.
-
I intend to write more about the challenges I faced with this project. ↩︎
-
Check Successor ML to see more. ↩︎
-
Poly/ML exposes a function that does just that: PolyML.print. The problem now being tied to a single compiler… ↩︎