How does BuckleScript's JavaScript interop work?
Tags: reason, ocaml, bucklescript
Published: 2019-01-20 03:05:30

---
title: How does BuckleScript's JavaScript interop work?
published: true
description: High-level overview of what the FFI mechanism does
tags: reasonml,ocaml,bucklescript
---

BUCKLESCRIPT has made a name for itself as a fast OCaml-JavaScript transpiler with good support for JavaScript interop. But how should you as a beginner get a handle on how it works?

The first thing to do is to bookmark the [Interop](https://bucklescript.github.io/docs/en/interop-overview) section of the BuckleScript docs. The second thing to be aware of is that these docs don't cover _everything_ that the BuckleScript interop enables you to do. For a more detailed reference, I recommend bookmarking the following:

- The original BuckleScript manual, in HTML form. It's slightly outdated but the 'OCaml calling JS' section covers some things that the docs site doesn't. https://raw.githubusercontent.com/BuckleScript/bucklescript/8a13b34fdba9f0b4d53f5433b66879df167c4217/docs/Manual.html
- glennsl's excellent cheatsheet gives an overview of most of the interop codes and their JavaScript outputs: https://github.com/glennsl/bucklescript-ffi-cheatsheet

Finally, an invaluable tool to bookmark is the 'Try Reason' online playground, where you can quickly experiment with interop code: http://reasonml.github.io/en/try.html

## Key ideas

To better understand the references listed above, it's helpful to have a mental model of what the BuckleScript FFI code does. Here's the _key idea:_ BuckleScript _bindings_ (interop declarations) are specifications that the BuckleScript compiler _mechanically translates_ into output JavaScript. Thus each binding needs to contain enough information to convert OCaml/Reason code _at call sites_ into output JavaScript. Over time, you'll learn what information goes where in each binding to produce the correct JavaScript. This can be slightly tricky but it helps tremendously to experiment and watch the JavaScript output (which updates almost instantaneously if there's no compile error).

Here's the second key idea: a binding is just a _declaration;_ it doesn't generate any output JavaScript by itself. The output is generated only at the _call site,_ or the place where the binding is used. This means for example that you can distribute a bindings package of pure OCaml/Reason code and the generated JavaScript will be produced _in its consuming package._

The third thing to remember that BuckleScript's interop is pretty powerful, which invariably means that there's usually more than one way to produce the JavaScript you need. Sometimes it's a matter of perspective as to which one is more correct; sometimes a matter of thinking through the _semantics_ (the _meaning_ of the JavaScript you wish to output), and sometimes just a matter of experience.

## Example

Here's a simple example that demonstrates the above ideas. I want to call `document.getElementById("main")`. How to go about doing that?

I'll break down the problem into parts. The first part is how to get `document.getElementById`. As you may know, `document` is always in scope inside a browser. And `getElementById` is always supported by the `document` object. So, in this case we can use the `[@bs.scope]` and `[@bs.val]` extensions: https://bucklescript.github.io/docs/en/bind-to-global-values#global-modules


```reason
type element;

[@bs.scope "document"] [@bs.val]
external getElementById: string => Js.nullable(element) = "";

/* This triggers the output: */
let main = getElementById("main");
```

Output:

```javascript
var main = document.getElementById("main");
```

Here's how the mechanical translation is happening, in terms of the output JS:

BuckleScript/Reason | outputs JavaScript
--- | ---:
`let main =` | `var main =`
`[@bs.scope "document"]` | `document.`
`[@bs.val] external getElementById` | `getElementById(`
`"main"` | `"main"`

This works, but it's a little incorrect because, semantically, `[@bs.scope]` is meant to be used for JavaScript _modules_ and `[@bs.val]` for _global values_ in modules.

## Example, approach 2

A better way is to model `document` as a globally-available _object_ with an _abstract type_ and a corresponding method `getElementById`, which is semantically what they are according to [the Web API](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById).

```reason
type document;
type element;

[@bs.val] external document: document = "";
[@bs.send.pipe: document] external getElementById: string => Js.nullable(element) = "";

/* This triggers the output: */
let main = getElementById("main", document);
/* Or: let main = document |> getElementById("main"); */
```

Output:

```javascript
var main = document.getElementById("main");
```

This is the exact same output but now I'm modelling the types and values differently in the Reason codebase. I explicitly have a `document` type that's declared to accept a method call `getElementById`. Here's the mechanical translation for this approach, again from the JavaScript perspective:

BuckleScript/Reason | outputs JavaScript
--- | ---:
`let main =` | `var main =`
`[@bs.send.pipe: document]`, `[@bs.val] external document: document = "";`, `getElementById(..., document)` | `document.`
`external getElementById` | `getElementById(`
`"main"` | `"main"`

Here I'm using the `[@bs.send.pipe: document]` extension to declare that the `document` type supports a method named `getElementById` and takes a `string` parameter. BuckleScript interprets this on the OCaml/Reason side as a function that can be called with a `string` and a `document`, but generates output JS that calls the appropriate method on the `document` with a `string` argument.

## Conclusion

BuckleScript works by using bindings to convert _idiomatic OCaml_ input (modules, types, functions, and values) into _simplified JavaScript_ output (values, functions, method calls etc.). These bindings are declarations that capture all the information that's needed to do the conversion, in a mechanical way. By trying out different bindings and observing the resulting JavaScript output, you can gain a lot of intuition on how to go about writing bindings that are idiomatic and semantically correct.