I learned a little SML recently. The SML programming language is special and interesting for the following reasons:

• It compiles to fast executables (unlike Python, Lisp, Ruby)
• It is strongly typed (unlike C, C++)
• It does not need a VM (no startup overhead unlike Java, C#)
• It is minimalistic (unlike OCaML)
• It is strictly evaluated (unlike Haskell)
• It has formal semantics
• It has an advanced module system (though not perfect)

Unfortunately there are some downsides as well, because it is not a main­stream language. Little documentation, few users, marginal tool support, many different compilers (though they are pretty much inter­change­able unlike Scheme interpreters) and quite academic tutorials make it hard to learn SML. Maybe documenting my first steps helps other interested people.

The first program was HelloWorld, but after some playing around the following code remained, which implements a factorial command line program:

```(* simple factorial function defined with IntInf for big number support *)
exception Undefined;
fun factorial n:IntInf.int =
if n<0 then raise Undefined else
if n<2 then 1 else n * (factorial (n - 1));

(* factorial function on strings *)
fun str_fac n =
case (IntInf.fromString n)
of NONE => "Argument is not a number."
| SOME m => n ^ "! = " ^ (IntInf.toString (factorial m));

(* calculate factorial of first argument or error message *)
val msg = str_fac (hd (CommandLine.arguments ()))
handle Undefined => "Undefined for negative numbers."
| Empty => "Need a number as an argument.";

print (msg ^ "\n");
```

Save that code as `factorial.sml` and compile it (for example using the mlton compiler). Calling it like `./factorial 5` will output `5! = 120`. It will also output various error messages, if it is not treated correctly. The binary is very fast with mlton and it seems to optimize the (naive) factorial function into a loop. At least big numbers do not produce a stack overflow.

The factorial function will raise an `Undefined` exception, if called with a negative parameter. This exception is handled by printing the `"Undefined for negative numbers."` message. The `Empty` exception is raised by `hd`, if the argument list is empty. Note that SML exception are not checked like Java exceptions, so they pass right through `str_fac`, which is a Good Thing™. I particularly like the expression-based syntax for exception handling in SML, which is more concise than try/throw/catch statements.

The case that the argument can not be parsed as an integer is handled via the Option structure (equivalent to the Maybe Monad in Haskell). This is an alternative to exceptions, which enforces the programmer to think about the failure case. With pattern matching SML provides an elegant syntax to handle the two cases `NONE` and `SOME m`.

My opinion about the various `IntInf` occurences is divided. They make SML use integers of infinite precision, instead of using `int` and raising an `Overflow` exception with bigger factorials. On the one hand they seem unnecessary and using `IntInf` instead of `int` seems to be a better default in my eyes. On the other hand the programmer has to think about the tradeoff now and the type inference makes it tolerable.

All in all I am pleased with SML so far. The code is concise and the type checker makes me think about the corner cases.