Back to OCaml…

Date Mon 31 August 2015 By Emmanuel Fleury Category programming

Why this, all sudden, passion to OCaml ?

I am working on the restart of a project that I wrote, with others, in C++. The code started to be a pain to handle and modularity was just totally lost on our way. As a consequence, any progress on the code was nearly impossible and getting freshmen (students) to work on it, just a nightmare.

I will not blame C++ for that, our choices and our way of writing the code is mainly responsible. But, C++ clearly did not help and got ridiculously complex to handle for almost no gain.

As I was thinking about the next language to go, OCaml raised as a good candidate for several reasons (one was that a similar project was written in OCaml). But, I will not detail all these reasons as the choice was purely random at the end anyway.

So, I started a bit writing programs in OCaml to try it out. I am not yet really totally comfortable with this language, but I start to get some understanding of what is going on.

Of course, I am not a beginner in this language. Long time ago, when I was a student in math, I wrote a few programs in OCaml and got acquainted to it. But, since then, I did a lot of imperative and object-oriented programming. So, I lost a bit my initial skills and my ‘purely functional programming‘ skills started to be a bit rusty (if not totally…).

Wetting my feet again in the OCaml language was somehow a pleasure, but also a pain, because I had to re-think my code in the functional paradigm where my imperative was ruling everything till now (and I am sure, I am still a bit too much imperative right now).

Well, changing our main programming paradigm from one to another is always useful to step back, I think.

So, the die is cast, lets go to develop in OCaml.

Build-system and Tests

First of all, I wanted to check that OCaml had good ways to handle automatic configuration and build of the programs, plus some default test framework that can make the job properly.

After browsing a bit among all the existing build-system frameworks I chose the OASIS framework. Quite complete, seems to be widely used and bring automation up to a level that allow to not care too much about the way you build your tool.

Concerning the tests, most of the projects I looked at were using the OUnit framework. The project lack a bit of documentation and examples, but seems to be almost feature complete and quite easy to handle.

As an example, I wrote a small vanilla project with an OASIS build-system, a few tests with OUnit and a few lines of code. I called it: OCaml-Boilerplate and made it available in my GitHub.

Factory method pattern in OCaml

Then, I tried to start thinking about the best way to program a few more advanced things like a factory method pattern, for example.

I found an interesting article (but a bit outdated now) that propose several implementations of various design patterns in OCaml. The shame is that it start to be a bit old and it does not take into account the first class modules, introduced in OCaml 3.12, which are simplifying a lot several patterns (but not the one I wanted). So, here is the code of the pattern:

(** virtual class interface a *)
class virtual a =
object (self: 't)
  method virtual foo: unit -> unit
end

(** Private classes to be kept out of sight from users *)
(** B concrete class *)
class b =
object (self)
  inherit a
  method foo () = print_endline "I am a B object !"
end

(** C concrete class *)
class c =
object (self)
  inherit a
  method foo () = print_endline "I am a C object !"
end


(** Version of the module A *)
let version = "0.1a"

(** Raised when given type is unknown *)
exception UnknownType

(** Factory method to select the proper class at runtime *)
let build choice = match choice with
  | "b" -> ((new b) : a)
  | "c" -> ((new c) : a)
  | _   -> raise UnknownType

This pattern is pretty much straight forward as it mainly relies on the Object-Oriented layer of OCaml and not really on the functional part of the language.

Once again, I have implemented this small example in my OCaml-Boilerplate project.

Command line parsing in OCaml and extension of the Std library

Finally, I try to build a simple command-line tool such that the command takes a set of options plus a mandatory argument (a file name). I quickly found the Arg module from the standard OCaml library, but I first found it quite basic and somehow not really usable without having to provide yourself some code (which should not be the case). So, I looked at existing extension of the OCaml Std (standard) library. I found mainly two:

  • OCaml Batteries: An open-source effort to extend the standard library (if you are familiar with C++, think about Boost). The Batteries library is quite furnished, quite well documented and open source. This library focus mainly on re-implementing the standard library with more efficient code and/or additional features such as the support for Unicode, or other useful data-structures, and many others.

  • OCaml Core: Also known as the Jane Street Capital’s standard library. In fact, Jane Street Capital is a trading company using intensively OCaml to develop programs for traders. This library has quite exciting addition to the standard library, especially a fully functional way of managing command line arguments (see this chapter in Real-World OCaml which is almost entirely dedicated to describe this library), that would have perfectly pleased me. But, this library is totally devoted to the need of the Jane Street developer and they will not consider your need if have some.

Here is a fairly detailled comparison between Batteries and Jane Street.

But, none of these extended libraries was really satisfactory, Batteries because it requires to re-implement almost everything by adding Bat in front of the basic module your are using. And, Jane Street’s Core because the installation process and the development of the library was a bit obscure to me.

So, I decided to make the effort to use the Arg module and to make it work as I wanted. In fact, I was not missing so much, I just wanted to ensure that the final anonymous argument (the filename) was mandatory.

I will not detail the way the Arg module is working, just assume that printing an error when you realize that the filename is not initialized was not straightforward and, more surprisingly, not a feature included in the command line specification.

And, I was also a bit surprised to find so few code sample (well, none in fact) on Internet trying to solve this problem. Yet, once you get your hands in it, it is just a game with the raise and try ... with ... constructions. Nothing really difficult, but yet requiring that an argument (which is not an option) is present seems to me something quite natural. So, I really do not understand why I did not find a single code snippet about it… Well, here the code I ended with:

(* Filename argument *)
let filename = ref ""

(* Default options *)
let verbosity = ref 0
let output = ref ""

(* version () : Display version and exit *)
let version () =
  printf "%s - %s\n" program_name program_version;
  print_endline "This software rebuild parts of the program control-flow";
  print_endline "based on an analysis of executable binary files.";
  exit 0;;

(** program usage *)
let usage = "usage: " ^ Sys.argv.(0) ^ " [-h|-V] [-v lvl] [-o file] filename"

(** program options *)
let speclist = [
  ("-output",  Arg.Set_string output, ": write output in file <file>");
  ("-verbose", Arg.Int (fun l -> verbosity := l), ": set verbosity level");
  ("-version", Arg.Unit version, ": display version and exit");
]

(** parse_args function *)
let parse_cmdline =
  begin
    Arg.parse speclist (fun x -> filename := x) usage;
    try
      if !filename = "" then
        raise (Arg.Bad ("missing argument: no input file name given"))
    with
    | Arg.Bad msg ->
       eprintf "%s: %s\n" Sys.argv.(0) msg; eprintf "%s\n" usage; exit 1;
  end

(* Main function *)
let () =
  begin
    parse_cmdline;
    printf "Running the rest of the program... with:\n";
    printf " verbosity = %d\n" !verbosity;
    printf " filename = %s\n" !filename;
  end

Finally…

To conclude about this experience in OCaml programming, I have been quite well surprised by the language and the libraries and frameworks associated to it. In a few lines and a few reading you get a build-system, a unit test framework up and running, and most of the usual software problems (selecting an object through a factory method, parsing a command line, reading a file, …) is quite easily handled.

For several years, I have been used to cope with the autotools, various weird C/C++ test frameworks and well known idioms to implement the features I just mentioned before. But, OCaml provide replacement for all these with a quite smooth learning curve for people having some experience in coding. Yet, I still feel that I have a lot of room for improvements in my way of thinking and, also, in the way I think about structuring my programs. Switching from imperative paradigm to functional will take some time (though, I am quite optimistic and eager to learn something new).

That was the good news, but what OCaml seems to lack is a better documentation and a bigger community to provide more programming idioms and on-line code snippets to guide beginners. Because, the language itself is quite nice and complete and doesn’t seems to have any obvious drawbacks. Moreover, the memory safety provided by the compiler makes the development much easier.

Another point, is that you will have to choose your side between the Core and Batteries libraries. Both are interesting for different reasons and necessary to ease development.

In fact, my personal feeling is that OCaml is way easier to develop with than C++ (I would not compare OCaml to C as their goals are quite different).

Also, I remember that during my first time with OCaml I was fighting a lot with the compiler and the deduced types of my functions… I do not know if I grew up a bit in programming but I did not had this feeling again this time. Maybe because as you get a better understanding of the program you write, you better understand the compiler.

Well, I (re-)enter a new World, I am impatient to code more now…