smlpkg tutorial
by Alex Nelson, 22 August 2024
There are few package managers for Standard ML. In fact, the only one I am aware of is smlpkg which lacks adequate documentation.
This post just constitutes a “note to self”, which I hope may be helpful to others.
Building smlpkg
This requires MLkit, MLton, or any other Standard ML compiler which can work with MLton’s “Basis build system”.
For MLton, you have to build it using the command:
~/src/smlpkg$ MLCOMP=mlton make clean all
Unfortunately, it does not build under Poly/ML or SML/NJ. I tried
using Chris Cannam’s sml-buildscripts,
but no dice. So if you want to build smlpkg using polybuild, you
have extra work to do for yourself.
Installing smlpkg
Assuming you somehow succeeded in building smlpkg…now what?
Well, if you tried make install, it will simply create a
subdirectory bin/ inside the smlpkg directory. So, for me, it
created ~/src/smlpkg/bin/ and placed a copy of the binary smlpkg
there.
You need to either place this in a place which is accessible to
$PATH (e.g., in /usr/local/bin/) or you need to add this directory
to the $PATH variable.
You can verify you’ve done this correctly by opening up a terminal,
and running smlpkg. You should get something like the following:
alex@lisp:~$ smlpkg
Usage: smlpkg [--version] [--verbose] [--help] <command> ...:
Commands:
add Add another required package to sml.pkg.
check Check that sml.pkg is satisfiable.
init Create a new sml.pkg and a lib/ skeleton.
fmt Reformat sml.pkg.
sync Populate lib/ as specified by sml.pkg.
remove Remove a required package from sml.pkg.
upgrade Upgrade all packages to newest versions.
versions List available versions for a package.
Using smlpkg
Assuming you have now built smlpkg and its directory is accessible
to PATH, how do we use it?
As I understand it, the intent is that:
- You keep your Standard ML projects in a subdirectory
$somewhere/lib/ - You must decide the location of the parent directory
$somewhere(usually it’s~, I guess?) - Starting projects.
Assuming you have a Github username
usernameand you wish to start a new project callednew-project, you run the command (in the directory$somewhere)smlpkg init github.com/username/new-project- Example: if I (with username
pqnelsonon Github) wanted to start a project calledindexer, then I would run the commandalex@lisp:~/src/$ mkdir indexer alex@lisp:~/src/$ cd indexer alex@lisp:~/src/indexer/$ smlpkg init github.com/pqnelson/indexer Created directory 'lib/github.com/pqnelson/indexer'. Wrote sml.pkg.This effectively creates a new empty directory
~/src/indexer/lib/github.com/pqnelson/indexer/and does some book-keeping in~/sml.pkgObserve that the directory which contains all my source code is
~/src/indexer/lib/github.com/pqnelson/indexer/— this is analogous to thesrc/directory in a Maven project. More importantly,smlpkgapparently expects to findlib/github.com/pqnelson/indexerwhen it tries to download a copy for any users of my code —smlpkgwill throw errors if it does not find this directory.If I want to add unit tests, I would create the subdirectory
~/src/indexer/lib/github.com/pqnelson/indexer/test/which will then contain atest.mlbfile,test.smltest runner file, and atests/subdirectory. - IMPORTANT: You MUST create a
sml.pkgfile which initially looks like:package github.com/pqnelson/indexer require { }Just change the uesrname from
pqnelsonto whatever your handle is, and change the project name fromindexerto whatever your project is called.This is an undocumented aspect to
smlpkgand it is important to do, otherwise other users will receive errors when they try to add your package to their projects.(I really do not understand why
smlpkgforces us to manage this file, nor why it lacks documenting the importance that this file exists.) -
IMPORTANT 2: You must version your projects with tags with prefix
v— so the initial release will bev0.0.1; without the leadingv,smlpkgwill throw errors when other people try to use your package.Again, this is not well documented, and it seems like
smlpkgshould check for versions without a leadingvprefix.
- Example: if I (with username
- Using packages.
If you wish to use my fancy
indexerpackage, you need to saysmlpkg add github.com/pqnelson/indexer— this will only modify the~/sml.pkgfile. You also need to run the commandsmlpkg syncto fetch the code.
Concluding remarks
Is this worth it? …I feat not so much for me, but hopefully for you.
But this also leaves much to be desired, since smlpkg lacks the
functionality of, say, Rust’s cargo or Clojure’s lein (or even
Java’s mvn).
For example, there’s no way to run tests with smlpkg, unlike the
other tools I listed off. But smlpkg isn’t designed to do that.
There’s a lot of repetitive work involved which I would have expected
a package manager to abstract away — for example, whenever I start a
new project, I always have to remind smlpkg I use github.com,
my username is pqnelson, and it always requires a
lib/github.com/pqnelson/<project-name> directory storing all the
source code for the project (as opposed to simply requiring a src
directory).
And this is entirely hardcoded into smlpkg, by the way.
The design decision seems well-motivated in the abstract: have a lib
subdirectory which stores the names of all the websites which have the
relevant git repositories, then the usernames are subdirectories of
these, and the library needed is the sub-subdirectory. This cleanly
separates out dependencies, and prevents name collisions (if the user
wanted to use both github.com/pqnelson/xunit and
gitlab/some-other-user/xunit, for some reason, then these two
packages are always separated from each other).
But it seems like forcing me to manage this in each package I develop
is foolish. Why not have a hidden directory ~/.smlpkg/cache/ which
does this for me? This avoids duplicate downloads (in case I write a
unit testing framework and use it in all my packages, for example:
smlpkg would download a copy for each package I develop).
I also don’t understand why smlpkg cannot keep track of those
packages and add them to the .mlb build files. This would allow me
to develop a package without redundantly bloated
github.com/pqnelson/new-package/ subdirectories.