Why We Use OCaml

INTRODUCTION

According to the TIOBE Programming Community index for June 2014, the following nine programming languages make up over 60% of the total popularity ratings: C, Java, Objective-C, C++, C#, PHP, Python, JavaScript, and Ruby.

Here at Esper, we have chosen to use the OCaml language for the server-side backend that implements most of our application’s functionality. OCaml includes many features that are not available in the more mainstream programming languages listed above, and we believe this gives us a competitive advantage. But even among Silicon Valley startups, OCaml is an unusual choice. The purpose of this post is to explain the benefits of OCaml and compare it to other languages. We hope to convince readers, especially other developers, to consider adopting OCaml for their projects as well.

 

PROGRAMMING LANGUAGE FEATURES WE LIKE

Here are some of the most important features of the OCaml language that we consider useful, or even essential, for building high-quality, reliable software:

First-class functions and immutable values

It is not always clear what people mean when they say “functional programming”, so we avoid this term and state directly what we want. Functions should be full-fledged values that can be created within other functions, stored inside data structures, passed as arguments, and returned as results. A function should automatically capture the relevant parts of its environment at the time of creation into a closure.

Most recent programming languages support these features, but they may be little used in idiomatic programs. One reason is that a function’s behavior is more difficult to predict when the values in its environment can be modified after its definition. Such modification could cause the function to produce different results when called with the same arguments, contrary to the expected behavior of a function. Mutability can be a useful tool in some situations, but it is often unnecessary and potentially harmful.


As a member of the ML family of languages, OCaml has its theoretical origins in the lambda calculus, a model where functions are so fundamental that everything else, even natural numbers, is built from them. OCaml uses a more efficient implementation, but functions are still taken very seriously, and they’re the primary building block for structuring programs.

OCaml does support mutable values like arrays and hash tables, but immutability is the default. Most programs are constructed with primarily immutable values, and the OCaml compiler and garbage collector are well optimized for this use case. While programmers learning imperative languages must contend with mutable state as soon as they write a while loop, the introductory book “OCaml from the Very Beginning” does not even introduce mutability until chapter 13 of a total 16.

Strong static type checking

Here we are concerned with two important properties of type systems. First, a language that performs implicit conversions between incompatible types is said to be weakly typed, versus a strongly typed language where conversions are explicit. C/C++/Obj-C, PHP, and to some extent Java all perform implicit conversions. This behavior may save a few keypresses, but programs are typically read more often than they are written, so we think it is better to be explicit about our type conversions, rather than require all programmers to memorize complex rules for narrowing integer conversions or a special truth table for the == operator.

Second, there is the question of when types are checked. No matter what language you use, all values have types in the sense of operations that they support. If a piece of code makes no sense due to incorrect type (even after implicit conversions), like an integer addition between a floating-point number and a string, or a method call on an object with no such method, the program gets stuck and cannot proceed safely. In a language without type safety, like those in the C family, the implementation may attempt to “do it anyway”, without defining what that means, and often with disastrous consequences. We are interested only in type-safe languages.

Many mainstream languages, such as Python, JavaScript, and Ruby, are called “dynamically” typed because they ensure type safety by checking all operations at runtime. If an operation fails to check, the program crashes with an exception. A “statically” typed language like OCaml or Java checks that all values have the appropriate type before the program is compiled. Since the program is known to be well-typed before it ever runs, no type checking needs to be done at runtime, improving performance. Moreover, modern static type systems can eliminate whole classes of errors that no test suite can cover. (When potential states are practically or literally infinite, testing can show only the presence of bugs, not their absence.)

In fact, given a flexible enough static type system (such as OCaml’s), the distinction between static and dynamic typing falls apart. It is easy in OCaml to define a type that covers everything, use this type everywhere, and perform your own runtime checks every time. A “dynamically” typed language is really just a “uni-typed” language in this sense, but a less powerful version where you are not allowed to define any other types. Prof. Robert Harper of CMU has been making this argument for a few decades. Types are an extremely useful tool, and we reject the notion that omitting static type checking is of any benefit to us. On the contrary, static typing gives us the powerful ability to make illegal states unrepresentable in our programs. Our frustrating, bug-ridden experience with dynamically typed languages like JavaScript has led us to conclude that they are inappropriate for serious development.

Algebraic data types and pattern-matching

Some programmers are turned off by static type systems, probably because the most popular ones are cumbersome to use, not particularly expressive, or both. OCaml and other languages in the ML family provide a convenient way to define complex data types by combining simple structures using products (ANDs, like a struct) and sums (ORs, like a tagged union). For example, an HTML document can be defined as:

type tag = string
type attributes = (string * string) list
type document =
  | Element of tag * attributes * document list
  | Data of string

This is the real type used in the OCamlNet HTML parser, not a simplified version. OCaml’s pattern-matching makes it easy to manipulate document values, deconstructing them according to the type definition. For example, here is a function to extract the text inside every <div> with class “foo” or “bar”:

let rec extract_text inside_div = function
  | Element ("div", [("class", ("foo"|"bar"))], children) ->
      String.join "" (List.map (extract_text true) children)
  | Element (_, _, children) ->
      String.join "" (List.map (extract_text inside_div) children)
  | Data str ->
      if inside_div then str else ""

Pattern-matching makes these common test-and-extract operations simple, and it also provides additional compile-time checks, like warnings about missing or useless cases. Such checks help make code refactoring in OCaml quite pleasant. In contrast, the typical object-oriented implementation of this in Java would involve multiple subclasses of a common base class. The methods for each case of extract_text would be spread across different files and organized around dynamic dispatch. A separate visitor object might also be used, further obscuring the control flow of the program.

Type inference

The function extract_text above does not include any type annotations for the variables it declares, including the parameter “inside_div” and the pattern variables “children” and “str”. No return type is declared for the function itself, either. Yet OCaml is able to infer the following type:

val extract_text : bool -> document -> string

Each call to extract_text takes a boolean (indicating whether this recursive call is inside a relevant <div>) along with the current node of the document to be processed, and it returns a string. As in a dynamically typed language, we didn’t have to write our types down just to satisfy the compiler, and yet type correctness is still enforced at compile-time. To do this, OCaml uses a modified version of the Hindley-Milner type inference algorithm, in which variable types are inferred from their usages in context, and any conflicts generate a type error.

 

MORE REASONS TO CHOOSE OCAML

None of the mainstream languages listed in the introduction provide more than two of the features described above. Some of them provide zero. OCaml has all of the above and many more advanced features, such as:

  • Parametric polymorphism (like generics in Java)
  • Polymorphic variant types, which require no type definitions and are still inferred automatically like magic
  • A module system supporting separate compilation, functors (module-level functions), and general modular programming with abstract types
  • Unique structural object system based on row types, with explicit subtyping
  • Labeled and optional parameters
  • Generalized algebraic data types (GADTs)

The OCaml implementation is mature; its codebase has been around for over 20 years and is maintained by the Gallium research team at INRIA. Its tools include both bytecode and native-code compilers, a replay-capable debugger, profiler, documentation generator, and lexer and parser generators.

OCaml’s community has seen some recent growth, with new books attracting more users. The OCaml Labs group at Cambridge is working on improvements to the core infrastructure while also developing a unikernel operating system in the language. Their efforts have produced lots of high-quality libraries, joining the many others already available in the OPAM package repository maintained by OCamlPro.

OCaml can also be compiled to JavaScript, so you can try it out right now in your browser. It’s a great time to be an OCaml developer!

18 thoughts on “Why We Use OCaml

  1. Thanks. Request topic of why (in your choice at one time) OCAML was better choice than
    Haskell. Please state your reasoning for multiple levels.

    Note: Of course, IMHO ML is the top… however for me at this particular time, with
    individual goals, HASKELL was EXCEED EXPECTATIONS. Granted, I was weakened
    by an emphasis on project management, electrical engineering, … joke – wine, women and song
    end joke… but

    also, if possible include in your discussion (for bonus points IN THE REAL WORLD)
    MirageOS, OCAML
    http://www.openmirage.org
    no endorsement and not even complete understanding claimed of system engineering
    (software) in OCAML.

    Thanks.

    Like

  2. I wanted to leave comparisons between OCaml and other ML-influenced languages to a future post, since this one was already long enough. The purpose of that post would also be different. Among languages that support all or most of the features I described, like Haskell and Scala and F#, the distinctions are more subtle. I’m not trying to start a flamewar with other language communities that share many of the same values and goals.

    Like

  3. Neovation chose OCaml over Haskell due to it being more pragmatic and generally better performing. Also, understanding OCaml makes it easy to write software in F#. This gives us access to a whole new platform and market – two for the price for 1 😉

    Like

  4. I’ve read several good stuff here. Certainly worth
    bookmarking for revisiting. I wonder how much attempt you put
    to make this type of wonderful informative site.

    Like

  5. Hi Jeff, I just wanted to thank you for your great post. I am picking up programming for a personal project after not having touched it for over 15 years. To “ease” myself into it I decided Perl would be a good way to start. However I soon found it frustrating to convert my thoughts of “what I want to do” into “how do I do it”.

    I decided I would start over, and learned about sooo many languages and different paradigms that we were not taught 15 years ago in Comp Sci. What I like about Ocaml is that it allows to express things very easily. I’m understanding now this term that I’ve seen everywhere lately about a language being “expressive”. I get it now and really see how Ocaml would be a better choice – at least I can see its potential for my project, which is parsing a makrup language I’m creating, and generating output to various targets (like Groff, Markdown, LaTeX).

    It seems like it’s trivial to define a syntax for a basic DSL just by using pattern matching, and this alone is worth its price in gold.

    Like

    1. Try F# – It’s a modern OCaml implementation into .Net, .Net Core, Mono and Fable.

      The syntax is even more easy to understand, thanks to operator overloading, local scope through indentation and a general clean up of things who turned out as confusing in OCaml, while both the code and user bases were already used to it.

      Much fun 😀

      Like

  6. Interesting read, I’m an erlang/elixir man myself, which I found while loking for alternatives to PHP/Python. I came across OCaml at the time but preferred Erlang’s concurrency model. Having learnt and used Elm recently, I’m finding the OCaml syntax much more familiar/friendly, will be giving it a second look.

    Like

  7. FSharp provides you with an OCAML like syntax including concurrency in .Net Core

    Elixirs pro is its “let it crash” philosophy, IMHO 🙂

    Like

Leave a comment