Now you might be wondering "what magic is this?? How did ocaml know that x was a string?" It doesn't. The type of x in this code is a magic type that will unify with anything! This is quite dangerous and can have cascading effects in OCaml's type inference algorithm.

The difference between the 2 %% from the previous section and the 1 % here is important! [%%something ...] is an OCaml "extension point" that represents a top-level statement (it can't show up inside a function or value, for example). [%something ...] is an extension point that stands in for an expression, and can be put just about anywhere -- but make sure that the JavaScript you put inside is actually an expression! E.g. don't put a semicolon after it, or you'll get a syntax error when you try to run the resulting JavaScript.

Dumping in a function & passing values

We'll need a little knowledge about Bucklescript's runtime representation of various values for this to work.

strings are strings, ints and floats are just numbers

an Array is a mutable fixed-length list in OCaml, and is represented as a plain javascript array.

a List is an immutable functional-style linked list, and is definitely the more idiomatic one to use in most cases. However, it's representation is more complicated (try Js.log([1,2,3,4]) to check it out). Because of this, I generally convert to & from Arrays when I'm talking to javascript, via Array.of_list and Array.to_list.

Of course, this function that I wrote in JavaScript could be ported over to Reason without much hassle.

Remember that this is an escape hatch that's very useful for learning so you can jump in quickly and make something, but it's a good exercise to go back through and convert things back into nice type safe reason code.

I've run into more than a few bugs because of raw JavaScript that I added to save time 😅.

Settling down and getting disciplined about things

So far we've been using bs.raw, which is a very fast and loose way to do it, and not suitable for production.

But what if we actually need to call a function that's in JavaScript? It's needed for interacting with the DOM, or using node modules. In BuckleScript, you use an external declaration (docs).

So let's unpack what's going on. We created some abstract types for the Canvas DOM node and the associated RenderingContext object.

Then we made a getContext function, but instead of @bs.val we used @bs.send, and we used an empty string for the text of the external. @bs.send means "we're calling a method on the first argument", which in this case is the canvas. Given the above, BuckleScript will translate getContext(theFirstArgument, theSecondArgument) into theFirstArgument.getContext(theSecondArgument, ...).

The empty string means "the JS name is the same as the name we're giving the external in BuckleScript-land" – in this case getContext. If we wanted to name it something else (like getRenderingContext), then we'd have to supply the string "getContext" so that BuckleScript calls the right function.

Wow! Notice how BuckleScript just inlined our pi variable for us? And the output looks almost exactly like it was written by hand.

Using existing JavaScript libraries

When folks write bindings for a particular JavaScript library, they'd usually publish it to npm. Head over to the Libraries to find out how to find these.

To use a library that does not have existing bindings, however, you'll want to first install the npm package as usual, e.g. using npm install --save <package-name>, then just go ahead and write your bindings. You'll probably find the bs.module FFI feature particularly useful; it emits the right imports or requires, depending on the JS compilation target's module format.

As an example, here's the entire source code of the bs.glob bindings (converted to Reason, the original is OCaml):