2.2. Compiling OCaml Programs#

Using OCaml as a kind of interactive calculator can be fun, but we won’t get very far with writing large programs that way. We instead need to store code in files and compile them.

2.2.1. Storing code in files#

Open a terminal, create a new directory, and open VS Code in that directory. For example, you could use the following commands:

$ mkdir hello-world
$ cd hello-world

Warning

Do not use the root of your Unix home directory as the place you store the file. The build system we are going to use very soon, dune, might not work right in the root of your home directory. Instead, you need to use a subdirectory of your home directory.

Use VS Code to create a new file named hello.ml. Enter the following code into the file:

let _ = print_endline "Hello world!"

Note

There is no double semicolon ;; at the end of that line of code. The double semicolon is intended for interactive sessions in the toplevel, so that the toplevel knows you are done entering a piece of code. There’s usually no reason to write it in a .ml file.

The let _ = above means that we don’t care to give a name (hence the “blank” or underscore) to code on the right-hand side of the =.

Save the file and return to the command line. Compile the code:

$ ocamlc -o hello.byte hello.ml

The compiler is named ocamlc. The -o hello.byte option says to name the output executable hello.byte. The executable contains compiled OCaml bytecode. In addition, two other files are produced, hello.cmi and hello.cmo. We don’t need to be concerned with those files for now. Run the executable:

$ ./hello.byte

It should print Hello world! and terminate.

Now change the string that is printed to something of your choice. Save the file, recompile, and rerun. Try making the code print multiple lines.

This edit-compile-run cycle between the editor and the command line is something that might feel unfamiliar if you’re used to working inside IDEs like Eclipse. Don’t worry; it will soon become second nature.

Now let’s clean up all those generated files:

$ rm hello.byte hello.cmi hello.cmo

2.2.2. What about Main?#

Unlike C or Java, OCaml programs do not need to have a special function named main that is invoked to start the program. The usual idiom is just to have the very last definition in a file serve as the main function that kicks off whatever computation is to be done.

2.2.3. Dune#

In larger projects, we don’t want to run the compiler or clean up manually. Instead, we want to use a build system to automatically find and link in libraries. OCaml has a legacy build system called ocamlbuild, and a newer build system called Dune. Similar systems include make, which has long been used in the Unix world for C and other languages; and Gradle, Maven, and Ant, which are used with Java.

A Dune project is a directory (and its subdirectories) that contain OCaml code you want to compile. The root of a project is the highest directory in its hierarchy. A project might rely on external packages providing additional code that is already compiled. Usually, packages are installed with OPAM, the OCaml Package Manager.

Each directory in your project can contain a file named dune. That file describes to Dune how you want the code in that directory (and subdirectories) to be compiled. Dune files use a functional-programming syntax descended from LISP called s-expressions, in which parentheses are used to show nested data that form a tree, much like HTML tags do. The syntax of Dune files is documented in the Dune manual.

2.2.3.1. Creating a Dune Project Manually#

Here is a small example of how to use Dune. In the same directory as hello.ml, create a file named dune and put the following in it:

(executable
 (name hello))

That declares an executable (a program that can be executed) whose main file is hello.ml.

Also create a file named dune-project and put the following in it:

(lang dune 3.4)

That tells Dune that this project uses Dune version 3.4, which was current at the time this version of the textbook was released. This project file is needed in the root directory of every source tree that you want to compile with Dune. In general, you’ll have a dune file in every subdirectory of the source tree but only one dune-project file at the root.

Then run this command from the terminal:

$ dune build hello.exe

Note that the .exe extension is used on all platforms by Dune, not just on Windows. That causes Dune to build a native executable rather than a bytecode executable.

Dune will create a directory _build and compile our program inside it. That’s one benefit of the build system over directly running the compiler: instead of polluting your source directory with a bunch of generated files, they get cleanly created in a separate directory. Inside _build there are many files that get created by Dune. Our executable is buried a couple of levels down:

$ _build/default/hello.exe
Hello world!

But Dune provides a shortcut to having to remember and type all of that. To build and execute the program in one step, we can simply run:

$ dune exec ./hello.exe
Hello world!

Finally, to clean up all the compiled code we just run:

$ dune clean

That removes the _build directory, leaving just your source code.

Tip

When Dune compiles your program, it caches a copy of your source files in _build/default. If you ever accidentally make a mistake that results in loss of a source file, you might be able to recover it from inside _build. Of course, using source control like git is also advisable.

Warning

Do not edit any of the files in the _build directory. If you ever get an error about trying to save a file that is read-only, you maybe are attempting to edit a file in the _build directory.

2.2.3.2. Creating a Dune Project Automatically#

In the terminal, change to a directory where you want to store your work, for example, “~/work”. Pick a name for your project, such as “calculator”. Run:

$ dune init project calculator
$ cd calculator
$ code .

You should now have VS Code open and see the files that Dune automatically generated for your project.

From the terminal in the calculator directory, run:

$ dune exec bin/main.exe

It will print Hello, World!

Tip

If you use ocamlformat to automatically format your source code, note that Dune does not add a .ocamlformat file to your project automatically. You might want to add one in the top-level directory, aka the root, of your project. That is the directory that has the file named dune-project in it.

2.2.3.3. Running Dune Continuously#

When you run dune build, it compiles your project once. You might want to have your code compiled automatically every time you save a file in your project. To accomplish that, run this command:

$ dune build --watch

Dune will respond that it is waiting for filesystem changes. That means Dune is now running continuously and rebuilding your project every time you save a file in VS Code. To stop Dune, press Control+C.