There already exist quite a few tutorials around on how to get started using language X, or framework Y. I want to add to this pile of helpful guides, by creating one of my own. A guide for a project that is still quite early on in it’s life, but which shows great promise for advancing the rate of OCaml adoption.
The project I speak of is Bucklscript, and it is by far one of the easiest to use versions of OCaml I have experienced to date.
Bucklescript is an OCaml to javascript compiler that aims to do 4 things:
- easily integrate with existing javascript software and eco-systems
- generate readable javascript files
- generate efficient javascript files
- create those readable and efficient javascript files extremely fast.
And when I say extremely fast, I mean extremely fast. From my previous experiences using OCaml, I knew that the OCaml compiler was fast, but I wasn’t ready for the Bucklescript compilers speed.
installation
The easiest way to install Bucklescript is to do so by using the node.js/npm toolchain.
The npm install --save bs-platform
command works equally well in an existing npm project.
The command yarn add bs-platform
also works if yarn is your preferred toolchain.
In addition to installing the Bucklescript platform into your project directory, you will also need to install the ninja build system into your development environment.
bare bones project
Now that you have Bucklescript installed in either an existing project,
or a new project,
the next step is to create some OCaml source files,
and a bsconfig.json
file that tells the Bucklescript build system how to create javascript files that will execute either in the browser, or on a node.js project.
I like to put my source files inside a src
directory.
And then create a hello world main.ml
file.
Using Js.log
here is to make it absolutely clear that this cde is meant to be compiled to javascript.
Normal OCaml code might use the print_endline
function.
With Bucklescript,
the compiler will translate that function into the equivalent console.log
function call.
Before we can build our source code,
we need to tell Bucklescript where each of our source files are.
This is done using the bsconfig.json
file.
The bsconfig.json
format is described in more detail in the Bucklescript documentation,
but I will just point out that the ocaml-config.sources[0].files
array can be replaced with a JSON object that describes a regular expression to match your source files,
for convenience if you end up needing to add many source files to your project.
building
Now comes the fun part. Run the build command as follows:
To compare, this is roughly the same performance (for a hello world program) that I observe when compiling C programs. However the speed benefits of Bucklescript scale much better as you need less code in general for the same functionality, and after having written a few thousand lines of code, I can attest that the build time has never exceeded 0.1 seconds.
I will also add that the normal OCaml bytecode compiler (optimised version) is roughly 1.5-2 times slower than the Bucklescript compiler, and the OCaml native compiler (optimised version) is roughly 10 times slower.
The Bucklescript compiler also handles incremental builds extremely efficiently:
By default,
Bucklescript puts the compiled javascript files into the lib/js
directory with the same directory structure as the input source directories.
So we can see that our Js.log function call is indeed turned into a console.log function call.
running
Since the output is pure javascript, the output files can be executed using the node utility, or they can be loaded into the browser through some other means (either by directly referencing them, or through some further javascript bundling system such as webpack)
Because of the extreme speed of the compiler, it is well-suited to being used in an incremental automated compilation setup. Since this setup is working within the npm/yarn eco-systems, I turned to the nodemon tool which does a very good job of watching a directory tree for file system changes and then re-running build commands and/or restarting server processes.
Now if I edit the source code in main.ml:
We can observe the compilation and execution immediately on save.
And finally verify my earlier claim that the print_endline/Js.log functions will output the same compiled code:
next
In summary,
I have outlined how to get up and running with a new project using the Bucklescript OCaml to javascript compiler.
This technique will also work for existing projects as long as there are no filesystem conflicts.
I also shared some tips for doing fast iterative development,
by using the nodemon
tool.
From here to get more practical use out of a Bucklescript code base we will need to creating bindings to javascript libraries such that they can be re-used from the OCaml source code, and then use those bindings to create efficient and type safe software that can be executed in the browser, or in a node.js command line process.
If you want to try Bucklescript without starting a project or downloading and installing the compiler, there is an in-the-browser demo where you can experiment with the OCaml syntax, and observe what the equivalent compiled javascript is.