Unsound & Incomplete

A Nice Thing About OCaml Syntax

Lots of people seem to hate OCaml’s syntax: Google “OCaml syntax beautiful” and you get about 90,000 results, while “OCaml syntax ugly” gets around 133,000 — the haters outnumber the enthusiasts by about three to two. In its defense, while I don’t know if I would say OCaml’s syntax is definitely not ugly — I don’t find it offensive, but I’ve been writing some form of ML for almost two decades, and familiarity breeds resignation — there are at least some niceties that, in my mind, ought to be acknowledged.

In particular, OCaml’s syntax for referencing elements within module namespaces is very general and convenient. In most languages, to reference an element within a namespace, you have two options. First, you can use a fully-qualified name. For example, to create an std::vector in C++, you’ll usually write something like this:

std::vector<int> v;

If you have lots of expressions that use the same namespace, this can get kind of tedious. Here’s a typical example using some items from the STL:

std::copy_if(v.begin(), v.end(), std::back_inserter(u), std::not_fn(f));

One way to eliminate the multiple instances of std is to temporarily import all names from std into the local scope:

{
  using namespace std;
  copy_if(v.begin(), v.end(), back_inserter(u), not_fn(f));
}

This works, but is heavy and over-powered: it adds three lines, and it imports all names in std for an entire block, while you may only want to import the namespace for the scope of a single expression.

OCaml addresses this by generalizing how elements of namespaces are referenced. In C++, the :: namespace resolution operator essentially takes a namespace on its left-hand side and a single identifier on its right-hand side. (I’m playing fast-and-loose with the definition, but this is morally correct.) In OCaml, the equivalent operator, ., also takes a namespace on its left-hand side, but takes an expression on its right-hand side; so, unlike in C++, you can use . to import a namespace over the scope of an entire expression — and only that expression.

Here’s an example using the List module:

1 + List.(filter f xs |> fold_left Int.max 0)

Note that the List module is only opened for the expression on the right-hand side of the +.


As an aside, F# cleans up OCaml’s syntax a little bit by dropping things like unnecessary in keywords in let bindings. This cleanup seems to have resulted in better stats: Googling “F# syntax beautiful” gets 129,000 hits, while “F# syntax ugly” gets 142,000; if people aren’t exactly singing the praises of F#’s syntax, there’s a thinner line between love and hate.