Back when I was studying computer science in Edinburgh (1995-99), we all learned the ML programming language, specifically Standard ML, which was developed in my CS department in the 70s. Even though I think most people on my course quickly decided they didn’t like it, it grew on me as I became more familiar with it (and it seemed pretty simple relative to Prolog, which we had to learn in our AI courses).
I hadn’t used it since I left university, but given the recent resurgence of interest in higher-level programming languages, with features like type inference, such as with Scala and F#, I decided it was time to indulge in a bit of nostalgia, and refresh my memory.
At university we had a book called ML for the Working Programmer, which I remember being pretty good, but I’d long-since lost it, so I went onto Amazon and purchased it again.
I then downloaded Standard ML of New Jersey, a popular and powerful SML compiler/interpreter. I had previously considered Caml, an alternate dialect of ML to Standard ML, but I couldn’t really get used to it, and I liked the fact that SML supports continuations, a feature useful for building highly concurrent applications. Furthermore, there is MLTon is an extremely powerful “whole-program” SML compiler that reputedly produces extremely efficient code.
So what does SML look like? Here is a simple recursive function to count the elements in a list:
fun count() = 0
| count(h::t) = 1 + count(t);
SML relies on pattern matching. The first line tells SML to return 0 for an empty list. The second line will only be reached if the first line doesn’t match, and will only match if the list has at least one element. It splits the list into its first element, called ‘h’, and the remaining elements, called ‘t’ using the ‘::’ operator. It then returns 1 plus the number of elements in the tail.
After typing this into SML’s interpreter, SML produces this:
val count = fn : 'a list -> int
This is the type inference at work. Without specifying a single type, SML has inferred that the function count takes a list of type ‘a (pronounced “alpha”) which can represent any type, and returns an integer. Note that SML has correctly inferred that the type of the elements in the list passed to count is irrelevant for the purpose of counting them.
Lets try something else, a sum function to add up the elements in a list:
fun sum() = 0
| sum(h::t) = h + sum(t);
The general structure is similar to count, but this time SML infers different types:
val sum = fn : int list -> int
As expected, it has inferred that the list must be a list of integers.
SML is a powerful language, and I’d recommend it for anyone keen to broaden their knowledge of programming languages.
Unlike Haskell it is “impure”, meaning that functions can have side-effects. Some people seem really hung-up on this idea of purity, but so far as I can tell it is more of a fetish which makes life more complicated, and with few practical benefits (whenever I ask people to explain what is so desirable about purity, the response is generally rather theoretical or hand-wavy).
I also think its nicer than Scala in some ways, for example in ML you can do pattern matching on function definitions, rather than having to use match. And of course, its way more mature than Scala.