Login

OCaml v. SML

Only the most superficial, surface-level comparisons. Why? Because the importance of these comparisons only increases more resolutely with time, whereas practical, tool-considering, implementation-considering comparisons can all reverse with time. For example,

I'm so old I remember when it was the "Standard ML" variants (PolyML, esp. SML/NJ) that had the "industrial support" and caml-light was that weird French dialect that "wasn't even standard (shudder)".

- Chet Murphy

Clean Math (SML) vs. Never Again Math (OCaml)

Ariane 5's first test flight (Ariane 5 Flight 501) on 4 June 1996 failed, with the rocket self-destructing 37 seconds after launch because of a malfunction in the control software. A data conversion from 64-bit floating-point value to 16-bit signed integer value to be stored in a variable representing horizontal bias caused a processor trap (operand error) because the floating-point value was too large to be represented by a 16-bit signed integer. The software had been written for the Ariane 4 where efficiency considerations (the computer running the software had an 80% maximum workload requirement) led to four variables being protected with a handler while three others, including the horizontal bias variable, were left unprotected because it was thought that they were "physically limited or that there was a large margin of safety". The software, written in Ada, was included in the Ariane 5 through the reuse of an entire Ariane 4 subsystem despite the fact that the particular software containing the bug, which was just a part of the subsystem, was not required by the Ariane 5 because it has a different preparation sequence than the Ariane 4.

- Ariane 5 (WP)

This was a French rocket and OCaml is the French ML, so there's a grudge rooted in ancient blood memory that requires OCaml to very strictly separate floating point and integer operations. Thus

let wait = ses.delay +. Unix.gettimeofday ()  (* obviously float *)
...
misses = ses.misses + card.misses;  (* obviously int *)

In Standard ML this would not be obvious even with the exact same types and expressions. SML has (non-extensible) overloading for math and numeric literals. https://smlfamily.github.io/Basis/top-level-chapter.html has a table.

Data description language

SML has generally familiar syntax. Tuples require parens, lists have comma separation. The non-standard vector syntax is relatively weird but still reminiscent of Common Lisp.

val tuple = (1, 2)
val list = [1, 2]
val array = Array.fromList [1, 2]
val vector = #[1, 2]  (* Successor ML *)

OCaml is generally off-putting but IMO cleaner and more useful. Tuples are created by commas only, and parens remain purely mathematical; lists and arrays have semicolon separation which resembles unit sequences and permits very clean tuple lists:

  [
    "Cairo", cairo;
    "London", london;
    "Beijing", beijing;
    "Moscow", moscow;
    "Mumbai", mumbai;
    "Nairobi", nairobi;
    "Jakarta", jakarta;
  ]

Exceptions: character and library constructors and record member lookup. SML's more off-putting with SOME #"A" vs. OCaml's more familiar Some 'A', and SML is much worse for #field record vs. OCaml's record.field

Punning

Both languages have multiple namespaces, permitting something like a variable named list without colliding with the type named list, rather than the ugly lst workarounds that you find in 'beautiful' languages having single namespaces. This is already punning, but OCaml puns all across the language: let-punning, record punning, function call punning, labeled tuple punning, which all work to reduce otherwise awkward stuttering in code.

let (let*) = Option.bind
let x = Some 5

(* set x' to Some 6 *)
let x' = let* x in Some (x+1)
(*let punning ^ *)

None of this is incompatible with SML, so Successor ML grabs record punning.