Shipping a (very simplistic) ReasonReact app20 Aug 2017
I’ve been enamored with ML family of programming languages for a while. However, and aside from the occasional Kata, I had never really shipped anything using an ML language.
Given the recent activity in the Ocaml community, however – mostly due to the communication efforts behind Facebook’s Reason – I felt compelled to give it another try (after a very short incursion during my undergrad), and I was in for a treat!
Getting Started & Reading Material
I went through Reason’s initial setup right around the time Jared Forsyth published a very detailed tutorial about getting started with ReasonReact and compiling Reason for the browser using BuckleScript.
Next steps & Troubleshooting
But after the initial setup I was on my own, and I had to start somewhere.
a custom template for
turned out to be a great way to bootstrap my project without the hassle of
writing boilerplate code, setting up tooling and spending hours figuring out
the tiniest of mistakes.
As any beginner going through the hurdles of unfamiliar paradigms, it wasn’t long until I hit some difficulties. What follows is an attempt to document the pitfalls that trapped me at first, which I hope will be useful for others trying to figure out rookie mistakes as they get started with Reason(React).
type variable cannot be generalized
In his tutorial, Jared describes making a stateful component, but when I attempted to migrate my stateless component to a stateful one, I was immediately greeted by the following, somewhat cryptic, error message:
Module build failed: Error: File "/path/to/src/app.re", line 5, characters 16-51: Error: The type of this expression, ('_a, ReasonReact.stateless, ReasonReact.noRetainedProps, ReasonReact.noRetainedProps) ReasonReact.componentSpec, contains type variables that cannot be generalized
The solution was simply to use state somewhere in any function of my component. Even
just destructuring state from the e.g. the single argument to
Now the reason why this happens is quite interesting: if you look at the
there’s one type variable
'state. That’s the variable that the error is referring to:
state is not used within the component definition, the compiler can’t
infer what we want its type to be. If used explicitly, then we’re literally telling
what that type variable should be.
An interesting note here is that
ReasonReact.statelessComponent doesn’t have
this problem. If we look at its type definition,
it’s almost immediately obvious why: there are no type variables in sight. The
compiler always knows that it will take a
stateless argument (which is
defined above in the file as being the
The team behind ReasonReact is well aware of this error and some other edge cases in the library, and actively working to fix them in the near future. For more information, there’s a section about this pitfall in the Ocaml FAQ.
Modules & Capitalization
Ocaml has a very interesting module system.
In short, modules are used to group together related definitions, and can be
arbitrarily nested. Interestingly enough, files too become modules, and one of
my earliest mistakes was related to their capitalization when
opening a module
from a different file.
As part of ReKeys, I define a file called
for grouping certain definitions related to interaction with the DOM and events.
When trying to open this file for consumption in another file,
I couldn’t get it to work.
The reason is that the module provided by a file is recognized by the compiler
with the first letter (and only the first letter) capitalized. So
Dom_utils and I was trying to
Dom_Utils. This is one of those mistakes that I’ll never make again, but
it was a head scratcher for a while there!
Inline type signatures
Having tried other languages in the ML family such as Haskell, Elm or PureScript, I struggled initially with how to annotate the types of my definitions. In Haskell, for example, types can be annotated above a function’s implementation, like below:
foo :: Int -> Int foo x = x + 1
In Ocaml/Reason, however, annotating the
foo function would either be done
inline, or in a
.rei interface file.
/* my_file.rei */ let foo: int => int; /* inline, in a my_file.re file */ let foo: int => int = fun x => x + 1;
Overall, my experience with Reason has been incredibly smooth. For a change, I love how the compiler acts as someone constantly looking over my shoulder telling me the amazing ways in which I can mess up what I’m doing.
The Reason community on Discord is ultra helpful and has been very patient with my constant newbie questions about anything Ocaml / Reason.
I’m excited to keep tinkering with Reason and eventually build something more serious. In the meantime, I highly suggest you give it a try.