Cool Things Reason Formatter Does

Cheng Lou
Cheng Lou
Jun 13, 2017 · 4 min read

Reason comes with a formatter that takes your badly written code and turns it into a well-indented piece of art:

Think of it as Prettier or Gofmt but for Reason.

Arguments about how to format code (and syntax in general) have plagued us since the dawn of programming; I’m not here to write about that. Instead, I’d like to show how refmt (“Reason format”) goes a step further and bakes in some of the wider-reaching semantic changes into the syntax itself, and why we do things this way.


JSX is a syntax that usually desugars to a function call, famously used in React for describing a UI tree. Reason has it too (React and Reason are both created by Jordan). The difference is:

  • We’ve built this JSX into the language-level.
  • It’s React-agnostic.

What does that have to do with syntax? Well, if you prettify your code by running refmt on the following:

MyComponent.createElement message::"hello" children::[] () [@JSX]

You’d get:

<MyComponent message="hello" />;

That’s a big syntax “formatting” refmt just did! So different that calling it formatting is almost a misnomer; but it’s indeed just a syntactic thing: those two lines are semantically equivalent. If you examine both abstract syntax trees, they’re identical. Get it?

For those of you who don’t use ReasonReact, that [@JSX] is just an attribute for hooking up macros. ReasonReact turns that agnostic createElement call into something React-specific. No standard, no extra tooling, no fuzz. Just one macro. We’ve pushed all this business from the meta-language level into the language itself.

If you’re not a fan of JSX, here’s a more mind-blowing example.

Function & Currying

I’ll assume you know what currying is. This section applies to vanilla OCaml syntax too.

The syntax sometimes shapes the semantics a whole lot. In Reason/OCaml, functions always take one, and only one, argument:

let increment = fun x => x + 1;

We simulate zero-argument function by passing an agreed-upon dummy argument called “unit”:

let logSomething = fun () => print_endline "hello!";
logSomething ();

() is syntax sugar for unit. We’ve now eliminated an edge-case (albeit often used) function calling convention while drastically simplifying the type system.

We simulate multi-arguments function call by giving a sugar to functions returning functions. This snippet:

let add = fun x => fun y => x + y;
let six = (add 5) 1;

refmt-ed, becomes:

let add = fun x y => x + y;
let six = add 5 1;

This is why you get currying for free:

let addTo5 = add 5; /* this simply returns `fun y => 5 + y */
let 6 = addTo5 1;

No complexity in the semantics nor the types. There was never any need to invent currying. We obtained multi-arguments functions by building up from a small block, rather than splitting up a big block and adding the mechanism of “currying” explicitly into a language.

Eh sure, but I can have it in my language too if I wrap the above with a nice syntax…

That’s not true for performance. The OCaml compiler (and BuckleScript) is able to “specialize” to the particular arity you’re using (proof here). So when it sees you call add 5 1, it’ll generate the code that actually applies a single function call with two arguments. Otherwise, you’d be naively allocating and calling n functions for a function of n arguments.

JS Objects

Last one! BuckleScript exposes an interop feature that allows you to write JavaScript objects directly within OCaml. Here’s what it looks like in Reason syntax:

[%bs.obj {name: "Lil Reason", age: 10}];

This’ll compile down to a JS object of the same shape, fully typed. Forget for a moment how crazily great this is; here’s what it looks like when you refmt it:

{"name": "Lil Reason", "age": 10};

And now it even looks like a JS object! We’ve decided that the macro “bs.obj” (which, again, is agnostic and entirely customizable) was used enough to first-class it as a syntax feature.

What’s the point?

Hopefully these were nice demonstrations that the discussion around syntax could a bit more advanced than the typical one surrounding whitespace correction! Especially the first and last examples: part of what Reason tries to accomplish is to insert a layer of iteration speed in-between the slow-moving language semantics and the fast-moving userland libraries. We can observe users and capture relevant patterns, upstream some into the macros, then upstream some of that into the syntax, and finally upstream the worthy parts into the language itself and remove the need for certain libraries and tools in the first place. We can also potentially go the other way around: remove deprecated features by downstreaming them into the syntax, then downstream them into macros, then into libraries, then nothing.

Try refmt-ing some code! Every time the code comes out looking slightly different, you might have learned a new (equivalent) idiom. Last example! Try formatting this:

switch foo {
| true => 1
| false => 2

What did you get? =)

Word of Caution

Do NOT abuse macros & other AST transforms. The more you operate on meta-code (the AST), the less intent the code itself conveys. Reason & BuckleScript curated and bundled a few ourselves through careful analysis of tradeoffs.

Have fun with Reason and see you in Discord!


Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store