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...↩