Rescript, Reason, Typescript, OCAML, Javascript, Help Me Choose!
Introduction
I’ve been writing plenty about Rescript this year, although I’ve taken a bit of a hiatus to focus on developing a couple (Rescript) open source libraries. I wanted to get back into blogging to answer one of the most confusing questions in the Rescript community: What is ReasonML and how does it relate to Rescript?
However, it has grown into something quite a bit bigger. Instead, we’ll be talking about the relationships between a collection of several interrelated languages, and I’ll give my opinion on which you should use.
I specifically want to discuss the current state of these languages, rather than digging into their history. I make recommendations that are based entirely on my opinion. To help guide your own decisions, I indicate my experience level with each language, as familiarity has a habit of generating both delight and disatisfaction.
Patreon
This series takes a lot of time to write and maintain, and I don’t always have enough motivation to keep it up. I’m not one for begging for money on the Internet, but if you’re interested in providing some of that motivation, I’ve set up a Patreon account. I thank you for your support.
Other articles in this series
With over a dozen articles and counting, I’ve created a table of contents listing all the articles in this series in reading order.
This article is a stand-alone informational piece not connected to any of the existing tutorials.
Our Contenders
We’ll be discussing Javascript, Typescript, Rescript, Reason, and OCAML.
These all broadly fall into two categories: Those that compile to Javascript, and those that compile to OCAML (and therefore native).
However, OCAML can compile to Javascript in a couple ways, so this dichotomy isn’t as clear cut as you might think. Also, there are dozens of languages that compile to Javascript, including multiple versions of Javascript itself, but I’ll only be touching on a couple of them.
Javascript
Let’s start someplace that is almost certainly familiar. Javascript is so ubiquitous seems impossible to code for any platform without at least being able to read the language.
People love to hate Javascript. Most people seem to think it is only popular because it’s the only thing universally supported on the web. I’m not a huge fan of the language myself, but Javascript has some strong points.
For one thing, the language is extremely mature, and has had billions of corporate dollars invested into it. The Javascript virtual machine works so well that nobody ever really thinks about the Javascript virtual machine. We just talk about “Javascript”.
One of my favourite things about Javascript is that language improvements are frequent and on a regular schedule. Each new version of the spec is better than the last. Cosidering how bad Javascript was to begin with, this is important!
Javascript’s killer feature is, of course, its integration with web browsers. Every personal computer sold today comes with a Javascript interpreter integrated into it. This cannot be said for any other programming language. If you have a computer, you have Javascript.
Honestly, the only things Javascript can’t do “good enough” are compiling cleanly to native code and parallel computation. This turns out to be a pretty big problem if you are doing certain types of data science or or writing an embedded controller or operating system, but Javascript is certainly capable of handling most work loads you might throw at it.
Of the languages discussed, Javascript is the one I know best. Indeed, the only language I know better than Javascript is Python, which isn’t on today’s agenda. I’ve been coding in Javascript since it was really bad, and I have had a bad opinion of it for all that time. I’ve come to accept it in the last few years, but I still think there are universally better options.
Javascript: Recommended Uses
In my opinion, Javascript should only be used as a compiler target. Human beings should not write code in Javascript. The obvious exception is “legacy code bases”, but even there, I recommend gradually migrating to Rescript or Typescript.
It is also my weak opinion that Javascript (via nodejs or Deno) should not be used for service development. In this case it’s very much just an opinion. Node isn’t particularly bad at services, but there are other, better, options. If you do have a reason to use node, I still think choosing a typed language that compiles to Javascript is a better decision.
And it is my very strong opinion that Javascript should not be used for pseudo-native GUI applications. I am looking at you, Electron.
OCAML
OCAML is the oldest of the languages under consideration, and I doubt any programming language invented in the last 15 years hasn’t taken inspiration from OCAML in one form or another. That said, relatively few people (including myself) actually know the language well.
I’d say the two defining features of OCAML are a functional paradigm and static typing with best-in-class type inferencing. I find it a rather strange looking language, but that’s more a reflection of my own language history than the language itself.
Like Javascript, OCAML is extremely mature and rock solid. It is still actively developed, and the core features have been in place for a quarter century. However, it has a relatively small community, and there aren’t a ton of libraries. In fact, I’d go so far as to say that the main use case for OCAML is to create other programming languages! Rust, for example, was bootstrapped from OCAML, and the Hack compiler is written in OCAML, amoung others.
OCAML’s main benefit over Javascript is that OCAML can compile to native code and does not need a virtual machine at run time. While this opens up a lot of options, OCAML isn’t used overly much for systems development or native applications.
I’ve read through OCAML tutorials and set up the compiler with hello world and the like, but I haven’t actually written even a toy app in it. Learning the language better is on my todo list.
You can use Js_of_ocaml to compile OCAML to Javascript, but I can’t imagine any situation where that is a good idea! Use Rescript instead.
On Static Typing
I want to delve a little deeper into the static typing thing. Static typing is overhyped by its proponents and over-disparaged by its detractors.
Static typing is great for documentation. I like not having to scatter @param
style declarations in documentation strings just to indicate that a dynamically
typed function is supposed to expect an integer. That’s just silly.
Extending that, static typing is great for finding documentation about types. Modern editors using language servers can easily find and display the types for functions you are about to call.
Some argue that static typing provides performance benefits, but I’m not convinced. Static types are easier to compile to native architectures, and are therefore typically more performant, but that doesn’t strictly mean that static typing is a necessary condition for performance.
A key benefit of static typing is that it acts as a sort of sanity check that 100% of your code is valid syntax. In dynamic programming languages, you need 100% test coverage to be confident that every line of code is syntactically correct. Static checks help get you that baseline test coverage for free.
One drawback of static typing is that it’s necessarily verbose. It sucks when a language forces you to “write the same thing over here that you wrote over there” for no reason other than to prove you spelled the word “integer” correctly. This is why OCAML (and it’s derivatives) outstanding type inferencing is so important. If the compiler can infer the type, you don’t have to write it in, and your code is better for it.
Static typing can also cost a lot of developer time when you’re trying to figure out what type a specific value or function should have. If you spend fifteen minutes trying to figure out how to satisfy a compiler even though you know your code is correct, static typing has failed you.
OCAML: Recommended Uses
In my opinion, everyone should learn OCAML and then avoid using it in production. Knowing OCAML will, I think, help make better decisions and think differently when coding in other languages.
However, I woludn’t use it in a production system, if for no other reason that hiring people for the role would be virtually impossible! Further, if I was going to build a production native app, I would do it in Reason (see below) instead, as it appears a bit more approachable.
Typescript
Typescript has become wildly popular in a short amount of time. It seems like every open source Javascript library I touch has recently been reimplemented in Typescript. This is probably because porting Javascript to Typescript is extremely easy; Typescript is (sort of) a superset of Javascript, so must Javascript code is Typescript code. You can start adding types to existing JS code gradually, and next thing you know, you have static type checking and useful hints in your code editor.
However, Typescript is an intentionally unsound type system. This essentially means that you can’t really trust any type the compiler gives you. In practice this isn’t a huge problem; I’ve been bitten by it several times, but unsoundly typed Typescript is far far better than Javascript for catching silly errors. Still, it feels like it distinctly lacks elegance.
I have a couple other beefs with the language. First, it’s bloody slow to compile. Building anything with Typescript just pulls me straight out of the flow. Modern tooling such as esbuild has improved this, but it’s nothing compared to what I’ve become used to with Rescript.
Second, Typescript is huge. Learning all the language features takes quite a bit of trouble. I particularly hate having to track down some esoteric syntax to satisfy the compiler when I know the code is behaving correctly (I dislike Rust for the same reason).
Typescript’s benefit of being a superset of Javascript is also a drawback.
Javascript was not designed for a strongly typed world, and Typescript
provides a lot of strange hoops to jump through in order to make Javascript
code fit the typed paradigm. Things like Omit
and Partial
are really useful
and common in Typescript code that has to interface with older JS code. They
satisfy the compiler, but such constructs do not make the code easier to reason
about. Further, I still have to search the web every time I want to sort out
typing on generic costructs.
I am reasonably familiar with Typescript. I’ve written entire real world applications in it and occasionally dive into Typecript code for my day job.
Typescript: Recommended Uses
If your other option is Javascript, definitely use Typescript instead. It is better in all ways and creates safer code with fewer bugs.
I personally believe that Rescript (discussed in a bit) is a better choice for developing, however. I actually think that Rescript will overtake Typescript in popularity at some point. But I could be wrong about that. I generally believe that popular languages and frameworks get better faster than good ones get popular. There is a lot to be said for doubling down on Typescript; it’s easy to hire for and easy to train in (though not to master).
A note on Flow
Flow is a direct competitor to Typescript and does exactly the same thing. It’s main advantage over Typescript is that it is soundly typed. However, this is outweighed by the disadvantage that Facebook is intentionally not developing flow for public consumption anymore. Typescript has won that race and open source Flow will never catch up. If you don’t work for and have no intention of applying to Facebook (though I can say from experience that it’s an amazing place to work), you probably shouldn’t bother with Flow.
Reason
ReasonML is best described as an alternate syntax to OCAML. It uses the exact same syntax as OCAML, and you can access the same OCAML libraries. Indeed, it even uses the same compiler toolchain.
Compared to OCAML, the primary benefit of Reason is that it is easier to learn and understand, especially if you are mostly familiar with more common languages including (notably) Javascript.
The drawback is that it has an even smaller community. Plenty of real companies use and support Reason (and the underlynig OCAML toolchain), but it is still rather niche.
Another drawback is that the Reason documentation and ecosystem is structured such that you need to have a decent understanding of the OCAML ecosystem in order to use and understand Reason. One of the reasons I suggested people learn OCAML above is that knowing it will make certain aspects of Reason development simpler.
I have a little more familiarity with Reason that I do with OCAML, which isn’t much. I intend to do some future articles on Reason to explore native apps, but I’m not sure when that will happen!
Reason: Recommended Uses
This is a pretty weak opinion, but I think Reason should be considered for backend service and native development, especially if the rest of your ecosystem is in Rescript.
I hinted (ok, it was pretty blatant, not really a hint at all) earlier that I’m not a fan of Electron. It is ridiculous to ship a separate copy of Chrome with every desktop app these days. I would be inclined to build something on Reason and Revery instead, if I was planning to build native UI. That said, I would look very hard at this decision for production code because it would not be easy to hire developers to maintain it!
Reason can compile to Javascript in two ways, but don’t use either of them. First, since Reason is equivalent to OCAML, you can use the aforementioned js_of_ocaml library to compile Reason to JS. Second, the Rescript compiler can currently also build Reason (and OCAML) code. This support is deprecated and you should not expect it to work for new libraries. Write your frontend code in Rescript instead.
Rescript
Ok, I’ve written a lot about Rescript and it’s no secret that I love it, so we can skip some of the intro. Rescript looks like it’s part of the OCAML ecosystem, but that’s not strictly true and is becoming less true every day. Rescript started out as a variation of Reason, but is now so much more. Valid Rescript syntax looks similar to Reason, but Reason is no longer backwards compatible with Rescript, and this gap is widening as Rescript continues to improve.
At this point, it’s best to think of Rescript as unrelated to OCAML. It is much more part of the Javascript ecosystem. It would be possible, but very strange to install an OCAML package into a Rescript-powered web app. However, the much more normal behaviour is to use dependencies from the Javascript (npm) ecosystem. Rescript is designed for Javascript interop; that’s its entire purpose.
In contrast to Typescript, Rescript is a very small, tight, tidy language. It is soundly typed with amazing type infreencing. It is incredibly fast to compile.
The one drawback of Rescript compared to Typescript is that it has a much smaller community. You have to write your own bindings most of the time. If you need support, your one and only recourse is the (very active, but small) Rescript forum.
Rescript is capable of interfacing with Javascript and with Typescript. You can generate bindings to existing Typescript code from Rescript and vice versa. So if you are thinking of migrating your codebase to Rescript, it’s very easy to begin. Conversely, if you decide to play with Rescript, it’s relatively straightforward to port your app back to Javascript or Typescript if you need to.
Rescript: Recommended Uses
Prefer Rescript in all situations that you would otherwise choose Javascript. In my opinion, you should also always choose Rescript over Typescript. However, there is some risk to choosing Rescript in that it is not yet super popular. I believe it’s going to take over the industry, but if it doesn’t, your organization will find it much easier to find Typescript developers.
Barring that caveat, I personally recommend Rescript for all web frontend development.
I am less certain about using it for backend development. If you are using node, do use Rescript instead of Javascript. However, I personally don’t like the nodejs ecosystem, and the real question is whether you should use node at all.
As I mentioned in the Reason recommendations, if you are using Rescript for your frontend, you may want to experiment with Reason for your backend. I don’t have enough experience one way or the other to explicitly recommend this right now. But stay tuned, I will be doing some experimenting with it soon!