ReasonML — React as first intended

ReasonML is the new tech that Facebook is using to develop React applications and promoting as a futuristic version of JavaScript (ES2030 they say). In this article I’ll take a brief look at this tech.

In a nutshell, ReasonML is:

  • A new way to write React applications;
  • A JavaScript-friendly syntax for the OCaml semantics;
  • Statically typed — with type inference;
  • Functional, but not pure;
  • Mainly compiled to JavaScript;
  • Backed by Facebook and Bloomberg.

React before React was different

React’s programming style is much closer to functional than to object-oriented programming. It’s thus not surprising to discover that the first React prototype was not implemented in JavaScript, but in Standard ML instead.

However, as the prototype was starting to mature, the author decided to port it to JavaScript and continue from there, because there were no mature transpilers to JavaScript and also because the world wasn’t ready to accept such a non-mainstream programming language and style back then.

As a result, React became popular as a technology linked to the JavaScript programming language.

Despite this success within the JavaScript ecosystem, some people started to feel that there was something going on behind the scenes and created other projects — such as Redux, Elm and Purescript — that pushed the community’s mindset closer to the originally functional and statically typed roots of React.

This made Facebook believe that it could be feasible and convenient to move React itself closer to its roots. Eventuay, they wouldn’t have done so if they didn’t have so much of the work already done…

Bucklescript

Some companies are developing such mission critical user interfaces that using dynamic or gradually typed languages could represent unbearable losses.

Bloomberg is one of such companies, and it was for Bloomberg that Hongbo Zhang was working and experimenting with the JavaScript runtime when he realized that it was not difficult to port the OCaml compiler to JavaScript and run it on the browser.

(* A Factorial implementation in Bucklescript / O'Caml *) let rec factorial n = if n <= 0 then 1 else n * fact(n-1)

The reality was that the OCaml compiler was already very modular and it wasn’t very hard to replace its native-code-generating backend by a javascript-generating one. With such backend that it was even possible to compile the OCaml compiler into JavaScript, thus self-hosting the Bucklescript compiler and running it in the browser.

Bucklescript was born and better yet, it was released by Bloomberg as opensource software.

(* A FizzBuzz implementation in Bucklescript / O'Caml *) let fizzbuzz i = match i mod 3, i mod 5 with | 0, 0 -> "FizzBuzz" | 0, _ -> "Fizz" | _, 0 -> "Buzz" | _ -> string_of_int i let _ = for i = 1 to 100 do print_endline (fizzbuzz i) done

It’s important to notice that the original OCaml compiler had already decades of development and optimizations done by the Institut National de Recherche en Informatique et en Automatique (INRIA), and it was one of the fastest compilers available for such an heavily typechecked language.

ReasonML

So, if Facebook intended to make the React ecosystem statically typed, Bucklescript was certainly a nice candidate, since they seemed to believe that JavaScript — with its popular curly braced syntax — was largely responsible for React’s success.

// A Factorial implementation in ReasonML let rec factorial = (x) => if (x <= 0) { 1; } else { x * factorial(x - 1); };

However, they weren’t naïve enough to simply take Bucklescript with its OCaml syntax. They rather keept the OCaml semantics; the Bucklescript backend and as much as they could from the JavaScript syntax.

In order to keep the JavaScript syntax they created an additional parser, handling a new language called ReasonML, that is simply OCaml with a javascript-like curly-braced syntax.

// A FizzBuzz implementation in ReasonML let fizzbuzz = (i) => switch ([i mod 3, i mod 5]) { | [0, 0] => "FizzBuzz" | [0, _] => "Fizz" | [_, 0] => "Buzz" | _ => string_of_int(i) }; for (i in 1 to 100) { print_endline(fizzbuzz(i)); };

The result is surprisingly similar to JavaScript, to the point that some JavaScript code can be directly processed by the compiler as if it was ReasonML and immediately enjoy the benefits of the statically typed compiler with no change to the code whatsoever.

// Both valid ReasonML and Javascript code let add = (a, b) => a + b; add(4, 6);

Reason React

Besides the work on the language and compiler itself, Facebook has also devoted some effort into developing a ReasonML wrapper around its React framework together with some additional functionality.

It’s called React Reason and has been developed so that it’s easy to mix JavaScript React components with Reason components within the same ReactJS or Reason application.

It should be noticed that React Reason isn’t simply a wrapper around React, but also provides out-of-the-box functionality that used to come with external libraries such as Redux and Immutable.

What’s up with Redux?

Redux is a state manager that’s very popular amongst React projects. Simply put, it allows organizing the application domain logic as a set of composed reducer functions, which are meant to express how the state of the application should be transformed as external events (such as user interactions).

When using ReasonML, we don’t need Redux anymore. ReactReason stateless components already come with the concept of a build in reducer, which is meant to take care of the problems Redux used to address.

/* * A simple incrementing counter in React Reason * try it at: http://bit.ly/counter-button-sample */ type state = {count: int}; type action = | Click; let component = ReasonReact.reducerComponent("Counter"); module Counter = { let make = _children => { ...component, initialState: () => {count: 0}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({count: state.count + 1}) }, render: self => { let message = "Clicked " ++ string_of_int(self.state.count) ++ " times(s)"; <div> <button onClick=(_event => self.send(Click))> (ReasonReact.string(message)) </button> </div>; }, }; };

How about Immutable?
 The functionality that used to be provided by Immutable is implemented at the language-level. ReasonML (and OCaml) operations are immutable by default, thus avoiding the cognitive and performance overheads of using an external library.

How does Reason compare to Elm?

A while ago I’ve written a series of articles about the Elm language and they are not that different from each other.

Analyzing their differences in depth is out of the intended scope of this article, but — in sum — they stem from a different stance about functional purity and from the different level of maturity of both projects.

Below you can find a tabular summary of how their traits match:

Common traits

  • Functional programming;
  • Compiled to JavaScript;
  • Safe;
  • Short feedback loop;
  • Easy to test and reactor;
  • Full-coverage, inferred static typing.

Differences

Trait Reason Elm Functional purity not pure pure Syntax JavaScript-based — optionally cleaner cleaner; OCaml-based syntax JS-Interop simpler — less safe safer — with more boilerplate Ease of testing Some code written in Reason might be harder to test due to its eventual lack of functional purity Always easy to test due to its functional purity React compatibility Yes No Dealing with JS side-effects easy to deal with side-effects by writing imperative code sometimes hard to elegantly deal with Polymorphism Parametric and oo-style Ad-hoc Parametric and Row-type Compiler speed Faster Slower Targeted platforms JavaScript; OCaml Bytecode; Native code (AMD; INTEL; ARM & PowerPC) JavaScript Industry/Academia Support Facebook; Bloomberg; INRIA Evan Czaplicki (Author); Prezi

Compilation to Native Code

As you may notice on the table above, it’s mentioned that ReasonML can be compiled to Native code. That can be done by using the ReasonML syntax layer with the remaining original OCaml compiler, including the original native-code backend.

There’s plenty of potential here, eventually allowing to share Reason code amongst the backend and frontend and compiling the backend to native code.

Real-world Reason

The flagship application for ReasonML is Facebook Messenger, which originally was a ReactJS application that has been progressively migrated to ReasonML. Additionally, there are plenty of other projects and companies listed in the React documentation page.

Conclusion

ReasonML seems like a new take on the same concept that has been explored by the Elm framework. Even so, the options taken by this project and its backers seem much more promising both from marketing and technological points-of-view.

While Elm seemed like a beautiful prototype built on innovative ideas, ReasonML seems the enterprise-grade realization of those, appropriately standing on the shoulders of giants and appealing to the taste of the mainstream.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

At Imaginary Cloud, we simplify complex systems, delivering interfaces that users love with our own Product Design Process. If you’ve enjoyed this article, you will certainly enjoy our newsletter too, which may be subscribed at our website. If there is any project that you think we can help with, feel free to reach us. We look forward to hearing from you!


Originally published at www.imaginarycloud.com on May 23, 2018.

Like what you read? Give Imaginary Cloud a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.