After recently setting up a new machine and going through the exercise of setting up a development environment for the nth time, I was frustrated by having to install and configure all of my plugins and dependencies to support auto-completion, syntax highlighting, and the other niceties I’ve come to enjoy with Vim. Looking for an alternative, I was drawn to Visual Studio Code (VS Code) with the VSCodeVim extension. Combining Vim key-bindings with Code’s excellent extension marketplace, I was able to recreate and in some cases improve the development environment I enjoyed as a Vim user in VS Code. This blog post continues that discovery by setting up a brand new OCaml project and development environment in VS Code.
Setting up VS Code
OCaml has a rich set of tools for development like merlin for code completion and ocp-indent for code formatting. These tools come with Vim and Emacs integration, but lack support for Visual Studio Code out of the box. Some third-party extensions in the VS Code marketplace support OCaml, but many are deprecated or are targeted towards ReasonML and the Reason compiler chain. What to do? The ocaml-lsp project is a promising implementation of a language server for OCaml, and the extension OCaml Platform makes use of this language server in Visual Studio Code. This combination seems to be the best maintained option for OCaml on VSCode.
Start by installing
ocaml-lsp on your development machine through opam:
$ opam pin add ocaml-lsp-server https://github.com/ocaml/ocaml-lsp.git $ opam install ocaml-lsp-server
Next, install the OCaml
extension through the VS Code marketplace. Lastly, go to the OCaml Platform
extension settings and manually set the location of the
ocamllsp binary, which
should be part of your
~\.opam installation. Mine was set to
/Users/kevinsookocheff/.opam/default/bin/ocamllsp. Now, when editing OCaml
files, the OCaml Platform extension will automatically run the OCaml Language
Server, providing syntax highlighting, jump to definition, and error
This is all we need to do to setup VS Code for OCaml development. Next, let’s get the Dune system set up so that we can develop and publish our code.
Setting up a new OCaml project using the Dune build system
Dune is a build system for OCaml. A typical dune project
will have a
dune-project file and one or more
<package>.opam files at the
root level, and additional
dune files for libraries, executables, and tests.
Start by creating a new folder
hello_ocaml to house our project:
$ mkdir ~/hello_ocaml
Dune Project Layout
Done projects contain a file called
dune-project in the root directory. The
contents of that file name the project you are building and the dune syntax
(lang dune 2.5) (name hello_ocaml)
Next, create a directory
bin to hold your source code, and create a file
main.ml within that directory:
$ cd hello_ocaml $ mkdir bin $ touch ~/bin/main.ml
main.ml file can simply print “Hello OCaml!”. This is the OCaml
application that Dune will build for us.
let () = print_endline "Hello Ocaml!"
Building the Project
Given this scaffold, we can build the project using the
dune build command
from the project root. Doing so will create a
main.exe file in the default
directory (all Dune builds %mdash; even non-Windows — use the
$ dune build $ tree . |-- _build | |-- default | | |-- bin | | | |-- dune | | | |-- main.exe | | | `-- main.ml | | `-- dune-project | `-- log |-- bin | |-- dune | `-- main.ml `-- dune-project
You can run the executable as you would any other:
$ ./_build/default/bin/main.exe Hello Ocaml!
We can add libraries to organize our project using the same method we just
looked at for binaries. First, create a
lib directory and an initial library
$ mkdir ~/hello_ocaml/lib $ touch ~/hello_ocaml/lib/hello_ocaml.ml
Our library code will be fairly simple. It just removes the hard-coded message from the main executable and puts it in a library. (In the following sections bold-face font is the file you are working on, followed by the contents of that file).
let message = "Hello OCaml!"
to use this library, we need to declare the
dune file for it:
(library (public_name hello_ocaml))
and then update our
bin/dune files to use the library:
We also need to update the dune file to reference the library we added:
(executables (names main) (libraries hello_ocaml))
Modules in OCaml are referred to by the file name. We can use our library module
main.ml by referencing it. Dune makes this module part of our environment
when we add the
libraries stanza to the dune file in this directory. Modules
are required to have an upper-case first letter, so we have to start with an
upper-case to use the module.
let () = print_endline Hello_ocaml.message
Lastly, let’s build our project:
$ dune build File "lib/dune", line 2, characters 17-28: 2 | (public_name hello_ocaml)) ^^^^^^^^^^^ Error: You cannot declare items to be installed without adding a <package>.opam file at the root of your project. To declare elements to be installed as part of package "hello_ocaml", add a "hello_ocaml.opam" file at the root of your project. Root of the project as discovered by dune: .
Libraries need a
.opam file to before they can be used. We can declare one for
our project at the root level using the minimum Opam
integration required for Dune
projects. This minimum simply adds a
build clause to say “use Dune to build
this project”. The
jobs portions of this line are parameters that
get set from Opam during an installation.
opam-version: "2.0" version: "1.0" maintainer: "email@example.com" authors: ["Kevin Sookocheff"] homepage: "https://github.com/soofaloofa/hello_ocaml" bug-reports: "https://github.com/soofaloofa/hello_ocaml/issues" dev-repo: "git+https://github.com/soofaloofa/hello_ocaml.git" license: "Apache-2.0" build: [ ["dune" "build" "-p" name "-j" jobs] ]
Now we can build and run our project.
$ dune build $ ./_build/default/bin/main.exe Hello OCaml!
Now we have a project that builds, an executable, uses a local library, and that is integrated with VS Code. Happy coding!