March 17, 2019
When compiling Reason / OCaml applications to JavaScript, there are two main options: BuckleScript and Js_of_ocaml.
In this article, we will explain briefly the origins of each one of them, show the upsides and strong points of each solution, and lastly conclude with some guidelines on what use cases fit each one better.
Back in 2013, Jérôme Vouillon and Vincent Balat published the paper “From Bytecode to JavaScript: the Js_of_ocaml compiler”. The main idea was to use OCaml bytecode –an output that the OCaml compiler could produce already through the use of ocamlc
– to produce JavaScript code. The main motivation was that OCaml bytecode is a very stable format, so building on top of it would decrease the maintenance costs of Js_of_ocaml. Another upside of using bytecode was that it would make possible to leverage all the existing ecosystem of libraries and applications and make them run in the most ubiquitous platform that ever existed: the browser.
High-level view of the OCaml compilation process, highlighting the part that is handled by Js_of_ocaml
A couple of years later, Hongbo Zhang proposed in the Js_of_ocaml repository the idea of taking OCaml rawlambda output to produce JavaScript code. This was a very different approach from the one taken by Js_of_ocaml, as rawlambda is a data structure much more central in the compilation process. The motivation behind this approach was that JavaScript and OCaml share a lot of the language semantics, so by reaching into rawlambda, the compiler backend that would become BuckleScript could make the JavaScript output smaller, and keep it closer to the original OCaml code. This was impossible by design for Js_of_ocaml, which starts the conversion to JavaScript at the very end of the compilation process, because at that time a lot of information that would be valuable semantically has been erased already –like function names (Edit: as noticed by Louis Roché, Js_of_ocaml maintains functions names just fine, as can be seen by the source maps feature).
High-level view of the OCaml compilation process, highlighting the part that is handled by BuckleScript, in comparison to Js_of_ocaml
An so, two options to compile from OCaml to JavaScript were born. 😄
These two projects don’t compete against each other, even if it could seem so because they both produce JavaScript. In reality, their initial goals, target audiences and design decisions were so different, that this led to very different implementations and sets of trade-offs too.
The following is a non-exhaustive list of the benefits of each of them.
external
: for apps that need to be compiled to both native code using C externals, and also to the web using JavaScript externals, then Js_of_ocaml provides the best experience. The JavaScript functions can be declared with external
and Js_of_ocaml will take care of linking the underlying JavaScript functions.compiler-libs
(which allows to work with the OCaml compiler, parsetree, etc) that are not available in BuckleScript, are very easy to use in Js_of_ocaml. This allows to create in-browser applications that are mind blowing, like sketch.sh.array
, string
and boolean
have the same representation, while Js_of_ocaml needs to convert to Js.js_array
, Js.js_string
, etc. These conversions in Js_of_ocaml are annoying if you need to interop with JavaScript code very often.So, when to use each one? Here are some examples of situations where you want to use one or the other.
An existing Reason / OCaml native application that needs to be adapted to run on the browser A library or framework written in OCaml / Reason that needs to define externals to both C and JavaScript. For example: Revery. Applications that need access to the compiler internal data structures (parsetree, typedtree). For example: sketch.sh.
An existing JavaScript application that needs to be partially or fully migrated to OCaml / Reason. A web application written in Reason or OCaml that needs access to many existing JavaScript libraries. A library written in Reason / OCaml that is exposed to JavaScript consumers as well. For example: Nact.
I hope you enjoyed the comparison, if you want to share your experiences with BuckleScript or Js_of_ocaml or have a suggestion, reach out on Twitter.