Creating a YOCaml project (a site generated by YOCaml) requires a
ready-to-use OCaml development environment. In this guide, we will see
how to set up a reproducible base for using YOCaml. Before starting,
make sure that OCaml is installed, in particular by checking that
OPAM is available in your $PATH
. (You can
refer to the instructions
on the official site).
Aside on switches
A common approach is to sandbox your project using switches so as not to share dependencies with your other projects. For this guide, we will assume that you are using a switch dedicated to the tutorial. However, if you are already familiar with OCaml and OPAM, you are, of course, free to organize your dependencies as you see fit.
Create an empty switch for the project
Even though YOCaml is released for all OCaml versions >= 5.1.0
, we
use version 5.3.x
for the tutorials. You can run the following
commands in a shell from the directory where you want to create your
project:
mkdir my-first-blog
cd my-first-blog
opam update
opam switch create . 5.3.0 -y
eval $(opam env)
opam install dune
We start by creating (mkdir
) the directory where we will develop our
project, then move into it (cd
). We update the OPAM package
repositories with opam update
. We initialize a switch for version
5.3.0
of OCaml (the -y
flag validates all steps
automatically). Running opam env
sets up the necessary environment
variables for using the OCaml toolchain (associated with the
switch we just created). Finally, we install
Dune, the recommended package manager.
Once these commands are executed, the my-first-blog
directory will
be associated with a switch, in which all our dependencies will be
installed (sandboxed).
Project setup
Now that our switch is set up, we can actually put the project in place. There are several approaches to initialize an OCaml project (for example, by running a series of commands), but the approach we recommend here has the advantage of making the project easily cloneable and will simplify setting up continuous integration (to, for instance, automate the deployment of new versions of your site).
Although it is possible to initialize a project using Dune’s CLI
(dune init
), we will create the files manually in order to better
understand the different aspects of the setup and to have fine-grained
control over how the project is organized. However, if you are already
familiar with the OCaml ecosystem, you can stick to your usual
workflow. Since YOCaml is just a collection of regular OCaml
packages, no special ceremony is required.
Project description
The project is described in Dune through the dune-project
file. This
is a file where we describe a project's metadata and dependencies
(organized by packages) using
S-expressions. At the
root of my-first-blog
, let's create the dune-project
file:
touch dune-project
dune --version
The second line lets us see which version of Dune is installed in our switch, which will allow us to choose the version of the Dune language we will use for our project.
The Dune language is a fantastic tool that allows you to describe the features offered by Dune while ensuring a form of backward compatibility. As new versions of Dune are released, the tool remains compatible with previous versions of the language, allowing a modern version of Dune to run projects configured for an earlier version. If you are using version
x.y.z
, you should specify the version as(lang dune x.y)
.
Now that we know our Dune version, we can fill in the dune-project
file:
3
name
"your name <you@domain.com>"
"your name <you@domain.com>"
namesynopsis"My first blog using YOCaml""My first personal blog using YOCaml for
fun and profit"5
The first part of the file outlines how Dune will handle the project — essentially configuring the entire project. The two fields that can be a bit confusing are:
-
(generate_opam_files)
: This instructs Dune to create the OPAM description files. Dune is just the build system, and OPAM is the package manager. This field tells Dune to regenerate the OPAM files, centralizing the project configuration in ourdune-project
. -
(executables_implicit_empty_intf)
: This automatically creates an empty interface file for executables, allowing the compiler to track unused definitions. In practice, this is recommended, but optional.
The second part of the file specifies additional metadata. The
repository (here we used GitHub as the host)
allows generating additional fields (like a link to the bug
tracker). It also lists the project maintainers and authors, and
the license under which the project is distributed. Here, it is MIT
,
but a wide variety of license
identifiers are supported.
The final part concretely describes our package: its name,
dependencies, etc. In our case, for now, we only depend on OCaml, with
the Dune dependency inferred from the (lang dune ...)
field.
Development environment
If you started from a switch dedicated to the project, the
sandboxing means there is no pre-configured development
environment. The OCaml website provides an excellent introduction to
setting up a development
environment that details the
steps to configure your editor. To centralize dependencies (including
development ones), we can edit the package
section of dune-project
to include our development dependencies:
(package
(name blog)
(synopsis "My first blog using YOCaml")
(description
"My first personal blog using YOCaml for
fun and profit")
(depends
- (ocaml (>= 5.3.0))))
+ (ocaml (>= 5.3.0))
+
+ ;; Dev setup
+ (utop :with-dev-setup)
+ (ocamlformat :with-dev-setup)
+ (ocp-indent :with-dev-setup)
+ (merlin :with-dev-setup)
+ (ocaml-lsp-server :with-dev-setup)))
From our point of view, this set of packages is sufficient to provide a pleasant OCaml development experience, even if, in this tutorial, we may not use all of them:
-
Utop is a REPL that integrates very well with Dune. Running
dune utop
at the root of your project will start an interactive loop with your project and its available dependencies. -
ocamlformat is an OCaml code formatter that formats code according to a configurable style.
-
OCP-indent is an efficient tool that, among other things, calculates the cursor position after a line break by applying OCaml-specific indentation rules.
-
Merlin is a server providing IDE services (notably for Emacs and Vim).
-
OCaml-lsp-server is another server that provides IDE services based on LSP, allowing any editor with an LSP client to offer a pleasant OCaml editing experience.
If your editor is properly configured, our my-first-blog
directory
should be associated with a switch that has all the ingredients to
start using YOCaml!
Having both Merlin and OCaml-lsp-server may seem redundant, but from our point of view, it allows the development environment (associated with the
with-dev-setup
context) to be compatible with many workflows, enabling both Merlin and LSP users to have a functional setup. Additionally, since OCaml-lsp-server depends on Merlin, the dependencies are shared, and adding Merlin comes at a relatively low cost.
Code formatting
Installing Ocamlformat in a switch is not enough to enable
formatting; the tool must be configured. Adding a .ocamlformat
file
at the root of the project (in the my-first-blog
directory) is
sufficient:
touch .ocamlformat
However, you can of course configure your formatter in more detail by referring to its documentation.
Installing dependencies
Now that we have extensively described our project's development dependencies, we can install all of them. First, run:
dune build
This will update the OPAM file (generated from our dune-project
file). Next, we can ask OPAM to install all our dependencies in our
current switch (we reuse the -y
flag to automatically confirm all
steps):
opam install . --deps-only --with-dev-setup -y
Normally, everything should go smoothly. If not, you can find support through the various communication channels of the OCaml community, which is very friendly and responsive (and don’t hesitate to send us any missing information or requests for corrections/clarifications).
From our point of view, this setup is generic enough to bootstrap a
wide variety of OCaml projects.
At this point, we have only set up an OCaml development environment, but YOCaml has not been mentioned yet. Indeed, it is time to actually start using YOCaml.
A first use of YOCaml
Now that we have an environment (sandboxed or not, depending on your
preferences) suitable for writing OCaml, we can add YOCaml
dependencies. As with any time we want to add dependencies to the
project, we need to modify the dune-project
file (the section
dedicated to describing the package):
(package
(name blog)
(synopsis "My first blog using YOCaml")
(description
"My first personal blog using YOCaml for
fun and profit")
(depends
(ocaml (>= 5.3.0))
+
+ (yocaml (>= 2.5.0))
+ (yocaml_unix (>= 2.5.0))
;; Dev setup
(utop :with-dev-setup)
(ocamlformat :with-dev-setup)
(ocp-indent :with-dev-setup)
(merlin :with-dev-setup)
(ocaml-lsp-server :with-dev-setup)))
The tutorial assumes that we are using at least version 2.5.0
of
YOCaml, hence the version constraint, and since we assume the reader
is using a UNIX-like distribution, we install yocaml_unix
.
As with any modification to the dune-project
file, we need to
regenerate the OPAM file(s) and then install our dependencies in the
current switch:
dune build
opam install . --deps-only --with-dev-setup -y
The difference between the YOCaml package and the YOCaml_unix package will be explained in detail in the following sections, but we can briefly summarize:
yocaml
describes the functional core of YOCaml and allows you to define programs.yocaml_unix
is a runtime context; it takes a YOCaml program (defined using theyocaml
package) and executes it.
In general, the concept of a runtime, here yocaml_unix
, comes into
play at the very end of the project, once all tasks have been
defined. But don’t worry—all of these concepts will be fully detailed
in the next guide.
Now, we will actually use YOCaml in a simple example: we will just
log "Hello World"
to the standard output.
A first binary
As mentioned in the introduction, YOCaml is a collection of packages
that does not impose any particular ceremony. You can organize your
source code however you like. However, for the following guides, we
will assume that the sources of our program are located in the
my-first-blog/bin
directory:
mkdir bin
touch bin/dune
touch bin/blog.ml
As we mentioned in the dune-project
file to generate empty
interfaces for executables, it is not necessary to create a
bin/blog.mli
file. We can now modify the dune
file to describe our
binary:
public_namelibraries
Now, let's modify our blog.ml
file to use YOCaml to print Hello World
to the standard output. The goal of this step is to ensure that
our environment is set up correctly; in this section, it is not
necessary to understand what is happening in for the moment:
let program () =
Yocaml.Eff.log ~level:`Info "Hello World, from YOCaml"
let () =
Yocaml_unix.run ~level:`Debug program
Run the program using the following command:
dune exec bin/blog.exe
It should display Hello World, from YOCaml
on the standard
output. At this point, we have a functional working environment
that can be easily modified to add new dependencies! One final step
(particularly optional) would be to add a license file at the root of
our project and a README.md
file that explains, at minimum, how to
clone the project and initialize a local switch using the following
command:
opam update
opam switch create . --deps-only --with-dev-setup -y
We can now move on to the next section to explore the fundamental concepts of YOCaml and, finally, build our first blog!