For one of my recent projects (
multi-grep) I went through the work to
get Standard ML building in Travis CI. It turned out to be not too
hard—in fact, the hardest part is already done, and I’m happy to share
how it works.
The way I set up my builds, I can:
- build and test with both macOS and Linux
- build and test with both SML/NJ and MLton
- create executables, even with SML/NJ
- publish the resulting builds to GitHub as releases
Apart from some scripts to install things on each operating system, under the hood it’s powered by Symbol, which is a build tool for Standard ML I wrote which factors out most of the project-agnostic stuff.
The core setup
Rather than paste the code into a snippet here and wait for it to get
out of date, see my
multi-grep project on GitHub for all the
up-to-date files. In total, there are three files in that repo which set
the whole thing up:
- .travis.yml (kicks off the build)
- Brewfile (deps for macOS build)
- tests/travis-install.sh (deps for Linux build)
If you haven’t used Travis CI before, you’ll probably also want to check out the Travis CI docs to get a feel for how to actually set things up, and where these pieces fit in.
Why write a whole build tool?
I mentioned above that I’d written a build tool for Standard ML, called
Symbol. Why? It started as a shell script +
multi-grep and then I realized that these scripts could be useful in
any Standard ML project.
SML/NJ and MLton are already great compilers with their own build tools. It’s useful to be able to build a project with both (SML/NJ for faster builds and a REPL, and MLton for faster compiled executables). All Symbol really does is put SML/NJ and MLton behind a unified, very stripped down interface.
There’s more information in the README, but some key points:
- Symbol makes it easy to build and install executables, even with SML/NJ which traditionally uses heap images.
- Symbol is built on
make, so if no source files change, even recompiling with MLton is instant.
- Symbol also supports scaffolding new Standard ML projects, which is nicer than starting from scratch.
Again, there’s way more information in the README, so definitely check it out if you’re thinking about setting up a new Standard ML project. The usage looks something like this:
Why Standard ML in the first place?
- Type inference in Standard ML is a breath of fresh air.
- Data types let me wonder less about how things work.
- Pattern matching makes for concise, clean, and correct code.
Standard ML was my most commonly used programming language throughout all of my university courses, so there’s a definite soft spot in my heart for it. There are features that I wish it had sometimes, but it’s the only language that I’ve used that doesn’t feel fundamentally broken in some way.