There has been a lot of change in the OCaml -> JS landscape over the last couple of years, and I regularly see questions about what all the names mean. This is my attempt to sort out the world as I know it.
As you navigate the world of compiling OCaml to JS, most if not all of the following will come up at some point:
That’s a lot to remember, but each works in their own similar yet completely distinct way. They can also be mixed and matched as projects see fit. I’m going to use diagrams to attempt to explain all of this and hopefully provide some clarity.
With apologies to those who understand how over-simplified this is, here is a rough overview of how compilers work, specifically in the case of OCaml.
Code flows from top to bottom in three distinct phases:
- Syntax is read into an Abstract Syntax Tree
- Checks are done to confirm the AST has valid semantics in the OCaml language
- For example, type checking
- Optimisation is performed at this level too
- The AST is written out to a machine executable
- OCaml supports both native output, like a C compiler, and platform-independent bytecode output like a Java compiler (although not using JVM bytecode, OCaml has its own unique VM).
Are you with me so far? Good. Next we’re looking at the development that happened in 2011 with the creation of js_of_ocaml.
- It can be a bit slow, JSOO is effectively a second compiler
- The resulting JS is mostly unreadable machine code (since that’s more or less what JSOO had to start with). Source maps help here, but they aren’t a silver bullet.
In early 2016, Bloomberg open sourced their answer to this process. Instead of treating the compiler as a black box and working with the result, they dug in and replaced the output phase with Bucklescript.
This no doubt took a lot of effort to achieve, but the result has some unquestionable benefits:
- By working with the compiler internals, their output retains the structure of the original code
- Most if not all of the compiler speed is retained
Really the only downside I can see is that by working so deep in the compiler they have to chase new compiler releases (as I write this it is still based on the OCaml compiler released in July 2015). Not that this is a particularly bad thing as the OCaml compiler is fairly stable – and it has since been further mitigated as we’ll see in a moment.
But here is where the real fun begins. If you compare the reason diagram to earlier diagrams, you’ll begin to wonder if it could be combined with with either js_of_ocaml or bucklescript. And that’s exactly what happened. When I first looked at this, efforts were made to support both as ways to compile reason source code to JS. That’s still possible, but like all good communities a single recommended approach is appearing and the tools are moving in that direction.
In the last few months the community has settled on the solution that is very fast and produces output JS that can be very similar to the input reason code. Native compilation and tooling still uses the reason compiler, but bucklescript has added first-class support for reason syntax directly into their compiler.
Wow. Check out where we are now.
Come join the fun in the reasonml discord!