Binding a library in ReasonML
ReasonML provides a strong type system and tooling but currently does not have the library support to do everything you might want. This is where bindings come in, they allow for a typed interface between a library and your code.
In this post, I am going to walk you through the process of making a simple command line application in ReasonML that uses the Commander library. This will give you the basic understanding of how to make a binding and give you a practical introduction to some useful parts of the type system.
Let’s get this show on the road
I am assuming you have ReasonML installed on your machine. If you do not go to https://reasonml.github.io/docs/en/global-installation.html
bsb -init create-binding -theme basic-reason
cd create-binding
npm run build
node src/demo.bs.js
We should now have a basic ReasonML app running on your machine. :D
Our goals for the application are simple, we allow our customer to run our command and specify these parameters:
- Size
- Toppings
- Cheese, with type
- How many pizzas
Adding commander
npm install --save commander
Now that we have commander installed, let’s add a Commander module to handle our binding.
Now that we have a Commander module we can invoke it in our main file.
If you run the code right now the commander object will be output with all its parameters. That is not what we want, let’s make it output a pizza size!
Adding size selection
The first thing we need is to add an option to our program, this can be done by using the option method that is provided by Commander. The problem is this method is not registered in our type system and reason will not compile if you try to access it directly. Luckily we can create a binding to use the method and keep our code idiomatic to ReasonML.
With this new binding, handling the option is simple for our app
If you run this command the object output will have a new parameter corresponding to the size parameter. This is awesome, we now have our handler for the size option created. We just need to wire it up so that the handler actually fires.
We can do this by running the parse method, the problem is we never specified a type for our output. This can be done by adding a new anonymous type to our Commander.t and returning it as the type of the parse method.
Now our binding requires a type to be specified when created and returns that type at the end. This will allow us to create the other options very easily and have the typing we expect from ReasonML.
Adding the other options
This is all we needed from our binding and to make this work. Hopefully you see how great the binding system is, you can easily hack together enough binding to make a working product in a couple minutes.
Considerations
In most cases this is all you will need to create a new binding, I would recommend you try this on other libraries or try to better flesh out the commander library as practice.
This example I gave is about as simple as you could possibly work with, you could probably change a lot to add more safety. A lot of the bigger bindings do add more layers of separation over the core library to make it more idiomatic and remove the potential for type unsafe code. A few great examples of other bindings would be:
- ReasonReact — https://github.com/reasonml/reason-react
- Bs-Express — https://github.com/reasonml-community/bs-express
- Reason-Apollo — https://github.com/apollographql/reason-apollo
The BuckleScript manual has a large section on bindings: https://bucklescript.github.io/bucklescript/Manual.html#_binding_to_simple_js_functions_values
Closing Notes
Thanks for reading, I am new to writing blog posts, so hopefully, it was not horrible. I wrote this because I am a big fan of working examples and I thought someone might find this useful. If you think anything was wrong or could be better explained, PLEASE tell me.
All the code for this example is currently up at https://github.com/Hehk/example-reason-simple-binding