What makes Reason so compelling? What can Reason do that TypeScript or Flow cannot? Is it just about having a static type-checker? These are some of the questions I had when first getting started with Reason.
Why Reason?
Support for immutability and purity
Reason isn't just about having a static type system. Also important is the fact that Reason is immutable by default. Immutability is an important concept in functional programming. In practice, using immutable data structures (data structures that can't change) results in safer, easier-to-reason-about, and more maintainable code than their mutable counterparts. This will be a recurring theme throughout this book.
Purity is another important concept in functional programming. A function is said to be pure if its output is determined only by its input, without observable side-effects. In other words, a pure function doesn't do anything outside of returning a value. The following is an example of a pure function:
let add = (a, b) => a + b;
And, this is an example of an impure function:
let add = (a, b) => {
Js.log("side-effect");
a + b;
};
The side-effect in this case is writing to the browser's console. That's why, in our preceding Hello World example, BuckleScript included the /* Not a pure module */ comment at the end of the compiled output.
Mutating a global variable is also a side-effect. Consider the following JavaScript:
var globalObject = {total: 0};
const addAndMutate = (a, b) => globalObject.total = a + b;
addAndMutate(40, 2);
/* globalObject now is mutated */
The global object was mutated, and now its total property is 42. We now have to be aware of all areas where this globalObject is mutated whenever using it. Forgetting that this object is both global and mutable can lead to hard-to-debug problems. One idiomatic solution to this problem is to move globalObject into a module where it's no longer global. This way, only that module has access to it. However, we'd still need to be aware of all areas within this module that can update the object.
If globalObject was immutable instead, there would be no way to mutate it. Therefore, we wouldn't need an awareness of all the areas that can mutate globalObject, since there wouldn't be any of these areas. We'll see that, with Reason, it's fairly simple and natural to build real applications in this way by creating updated copies of the original data. Consider the following:
let foo = 42;
let foo = foo + 1;
Js.log(foo);
/* 43 */
The syntax feels quite natural. As we'll see later in this book, immutability—changing by returning updated copies instead of applying destructive changes in place—fits the React/Redux way of doing things quite well.
The original foo was not mutated; it was shadowed. Once shadowed, the old foo binding is unavailable. Bindings can be shadowed in local scopes as well as global scopes:
let foo = 42;
{
let foo = 43;
Js.log(foo); /* 43 */
};
Js.log(foo); /* 42 */
let foo = 43;
Js.log(foo); /* 43 */
Trying to mutate foo results in a compilation error:
let foo = 42;
foo = 43;
/* compilation error */
We can see that immutability and purity are related topics. Having a language that supports immutability allows you to program in a pure way without side-effects. However, what if there are times when purity would cause the code to become more complex and harder to reason about than using side-effects? You may be relieved to learn that Reason (interchangeable with OCaml throughout the rest of this book) is a pragmatic language that let's us cause side-effects when needed.
It's also important to know that immutability doesn't come at the cost of performance. Under the hood, there are optimizations in place that keeps Reason's immutable data structures fast.
Module system
Reason has a sophisticated module system that allows for modular development and code organization. All modules are globally available in Reason, and module interfaces can be used to hide implementation details when needed. We will be exploring this concept in Chapter 5, Effective ML.
Type system
Reason's type system is sound, which means that, once compiled, there won't be runtime type errors. There is no null in the language, nor are there any bugs related to null. In JavaScript, when something is of the number type, it can also be null. Reason uses a special type for things that can also be null, and forces the developer to handle those cases appropriately by refusing to compile otherwise.
So far, we've already written some, albeit basic, Reason code without even talking about types. Reason infers types automatically. As we'll learn throughout this book, the type system is a tool that provides guarantees without getting in our way, and when used properly, can allow us to offload things to the compiler that we used to keep in our heads.
Reason's support for immutable programming, sound type system, and sophisticated module system are big parts of why Reason is so great, and there's something to be said about using all of these features together in one language that was built with these features in mind. When Facebook initially released React, they asked us to give it five minutes (https://signalvnoise.com/posts/3124-give-it-five-minutes) and, hopefully, that same frame of mind will pay off here as well.
Cross-platform
Building React applications with Reason is a lovely experience and, what's more, since OCaml is able to compile to native, we will be able to use these same skills to build apps that compile to assembly, iOS/Android, and much more. In fact, Jared Forsyth has already created a game called Gravitron (https://github.com/jaredly/gravitron) that compiles to iOS, Android, web, and macOS from one Reason codebase. That being said, the frontend JavaScript story is much more polished as of this writing.
Maintainability
Reason may take some time to get comfortable with, but you can think of this time as an investment in the maintenance and confidence of your future product. Although languages with gradual type systems, such as TypeScript, may be easier to get started with, they don't provide the sorts of guarantees that a sound type system such as Reason's can provide. Reason's true benefits cannot be completely conveyed within simple examples, and only really shine when they save you time and energy in reasoning about, refactoring, and maintaining your code. Put it this way; if someone told me they were 99% sure a spider wasn't in my bed, I would still have to check the entire bed because I don't like bugs!
As long as you're 100% in Reason and your code compiles, the type system guarantees there will be no runtime type errors. It's true that when you are interoperating with non-Reason code (JavaScript, for example), you introduce the possibility of runtime type errors. Reason's sound type system allows you to trust that the Reason parts of the application won't cause runtime type errors, which therefore allows you to focus extra attention on ensuring that these areas of the application are safe. In my experience, programming in a dynamic language can feel noticeably dangerous. Reason on the other hand feels like it always has your back.
Interoperability
That being said, sometimes—and especially when first learning about type systems—you may be unsure as to how to get your code to compile. Reason, through BuckleScript, allows you to drop down to raw JavaScript when you need to, either via bindings or directly inside your Reason (.re) files. This gives you the freedom to figure things out as you go along in JavaScript, and then once you're ready, convert that section of the code to type-safe Reason.
BuckleScript also lets us bind to idiomatic JavaScript in a very reasonable way. As you'll learn in Chapter 4, BuckleScript, Belt, and Interoperability, BuckleScript is an incredibly powerful part of Reason.
ES2030
Writing in Reason feels like writing in a future version of JavaScript. Some Reason language features, including the pipe operator (https://github.com/tc39/proposal-pipeline-operator) and pattern matching (https://github.com/tc39/proposal-pattern-matching), are currently being proposed to the TC39 Committee to add into the JavaScript language. With Reason, we can take advantage of these features, and much more, today.
Community
The Reason community is, hands down, one of the most helpful, supportive, and inclusive communities I've ever been a part of. If you have a question, or are stuck on something, the Reason Discord channel is the place to go for realtime support.
Often, when starting with a new technology, talking to someone with experience for five minutes can save you hours of frustration. I've personally asked questions at all hours of the day (and night) and am so incredibly grateful for and amazed by how quickly someone helps me out. Take a moment to join the Discord channel, introduce yourself, ask questions, and share your feedback on how to make Reason better!
The Future of ReactJS
In practice, few real-world applications use just ReactJS. Additional technologies, such as Babel, ESLint, Redux, Flow/TypeScript, and Immutable.js, are typically brought in to help increase the maintainability of a codebase. Reason replaces the need for these additional technologies with its core language features.
ReasonReact is a Reason library that binds to ReactJS and provides a simpler, safer way to build ReactJS components. Just like ReactJS is just JavaScript, ReasonReact is just Reason. Additionally, it's easy to incrementally adopt because it was made by the same person who created ReactJS.
ReasonReact comes with a built in router, Redux-like data management, and JSX. You'll feel quite at home coming from a ReactJS background.
It's important to mention that Reason/ReasonReact is already being used by several companies in production, including within one of the largest codebases in the world. Facebook's messenger.com codebase is already over 50% converted to ReasonReact.
As a result, new releases of Reason and ReasonReact come with code mods that automate much, if not all, of the upgrade process for your code base. New features are thoroughly tested internally at Facebook before they're released to the public, and this results in a pleasant developer experience.