ReasonML: A trial by combat
A somewhat contrived case study of trying to build stuff with Reason
Reason, in case you haven’t heard of it, is Facebook’s latest attempt to make writing javascript safe. It is a functional programming language which gets transpiled to type safe Javascript.
Now, javascript transpilation in the name of safety is nothing new; technologies like flow and typescript have tacked a type system onto javascript to make it easier to reason about. However, reason caught my eye for its seemingly backwards design; instead of trying to add a sound type system to javascript, it adds javascript to a sound type system, namely OCaml’s.
So, I decided to give it a try. I set out to build something with reason that was something slightly more than involved a Hello World application, and test how the language fared in somewhat non trivial applications.
The Project
This exploration dovetailed nicely with a recent frustration of mine: nerdy note taking was not something that I’ve found good options for (I love note taking with markdown, and it bums me out that it seems one else does). So I figured I’d build my own. Enter maple notes, a simple note taking app web app which supports markdown! You can find the source here, and the actual project is hosted here, if you’re interested.
The Good
From the get go, reason feels quite nice. Coupled with it’s sister project, reason react, it’s honestly amazing how painless it was to get started building the project. The syntax takes a bit of getting used to, but many of the concepts from react and javascript map over quite nicely. Having types for reason components as well as javascript also helped boost my productivity significantly, particularly when it came to refactoring and splitting up my project as I needed more and more components. For the majority of common cases, Reason feels well polished and thought out, and the build system runs **insanely** quickly, so the write / build / feedback cycle is comparable to that of vanilla react, if not faster.
Another very nice aspect is how well the majority of things interoperate. If you have preexisting components or javascript that you want to use, reason provides very good bindings to allow you to use those components while still writing reason. And, just in case, you can even drop into javascript if you really, really need to.
The Bad
As I delved into some of the more niche requirements for my application, it started to get a bit more challenging:
The first issue I ran into was animations. Expandable menu bars don’t feel right without a bit of animation, and unfortunately the docs don’t really have much to go off of to help.
But, reason does have a reasonably good interop story for most common cases. This means that you can wrap React components and JS libraries to work with Reason. I chose to use react spring for my animations, but, getting it to play nice with Reason was not trivial. A pattern that makes a lot of sense for an animation library is passing a function down as the component of a child, so that way the animation component can tweak the parameters of that function to animate the UI. Unfortunately being a strongly typed language meant reason wanted a react component as a child, and well, a function just isn’t that.
I ended up writing a wrapper component in vanilla react to handle the animation, and exposing that to reason instead. It worked, but it doesn’t feel great to have to resort to raw javascript just to get animations working. If you need a lot of animation or other more complex elements in your application, the headache may not be worth it yet.
The Ugly
Overall, it wasn’t too bad to get the UI side of things working. A couple hiccups and having to “cheat” by using normal javascript here and there, and the job got done. Since this exercise was primarily a UI thing I didn’t want to spend too much time on the backend, and just elected to save stuff to firebase every once in a while.
However, this ended up taking about half the total time I spent working on this project.
Bindings for a single javascript function or react component are well established and documented. Bindings for an SDK? Much less so. I spent more time than I care to admit trying first to write bindings directly to the SDK, and then later writing all the functionality I needed in raw javascript. Neither seemed to work consistently for me, and the documentation for it makes it challenging to grok. I later discovered that reason has its own package manager, which already contained some bindings and saved me from nearly giving up on getting firebase to work with reason. Regardless, I had to update a lot of the binding to work with the latest firebase APIs, and unfortunately debugging bindings is often complete guesswork.
Unless you really know what you’re doing, writing bindings for an entire SDK right now is hard. The process isn’t well documented, there are limited examples, and the dev console doesn’t give you a whole lot to work with. I hope this changes and looking back is one of the things I wish there was more explanation for, since I think it would really improve the experience of using reason for applications that need to interact with significant bits of javascript.
But all in all, it’s better than vanilla
Many of the challenges are expected with a new technology like Reason. There’s a couple of papercuts and rough edges that make the work more challenging than it really needs to be. But with that said, the vast majority of the time, it feels amazing. I have confidence that my application works (at least, most of the time), and it was much easier to do refactors and add features since there was a compiler at my back to help me along the way. For being such a novel idea, the language is really not too hard to pick up and use for the majority of the same things that I’d reach to javascript and react for (and in fact I think I’ll be reaching for it more after this!).
The main question I think I have while watching reason evolve is to see how effectively reason allows you to leverage existing javascript frameworks and libraries, and to do so ergonomically. Development doesn’t exist in a vacuum, and being able to pull in libraries when there is such a vast array of libraries available for javascript in a way thats quick, easy and typesafe is crucial when competing for mindshare in web technologies. That feature alone has enormous impact for productivity; I don’t want to be spending days figuring out how to get reason to work with it when it would only be an npm install away otherwise.