My experiences building a URL shortener with OCaml and ReasonML

This is not a guide, just a summary of my experiences with a few, hopefully helpful, tips scattered throughout.

I recently published two projects to Github: a URL shortener redirection server and GraphQL API and a web client for the API. The server is written in OCaml and the client in ReasonML.

ReasonML is basically a dialect of OCaml with a strong focus on interop with JavaScript, especially React. With that in mind and since I started this project with OCaml, I’ll write first about OCaml and then finish with Reason. But since they are so similar, most of what I write about OCaml that doesn’t involve tooling or JS, is also applicable to Reason.

Why OCaml

I wanted to build a URL shortener. I looked at several open source solutions, but all of them were written in dynamic languages (PHP, Python, NodeJS) and were often setup in a way that wouldn’t exactly fit our needs (mainly a lack of analytics and pattern matching). At least the redirection server of a URL shortener should be fast. It’s hard to beat a native binary.

My goto strictly-typed, compiled language has been Swift. I’ve found it to be very practical and enjoyable to write in. But there definitely have been issues with maturity when using it to build services to run on Ubuntu servers. There’s also been a lot of churn in third-party libraries that I had relied on in the past. It’s left a bit of a bad taste in my mouth, and I’m kind of giving it a bit more time to bake before going back to server-side Swift.

If I wasn’t going to use Swift, then I wanted to use something functional. Out of curiosity I read through Learn You a Haskell for Great Good! a couple years ago. I enjoyed a lot of the concepts and brought them into my work with PHP, JavaScript, and Swift. But I did have trouble actually being productive in Haskell. The package management story was confusing, and even to accomplish a simple project, I found I needed to copy and paste a lot of things I didn’t understand.

In the time since then, I’ve seen OCaml come up in discussions on Hacker News and other places frequently, always in expression of positive experiences. It’s functional, statically typed, compiled, and mature. It hit what I was looking for and I didn’t see a reason not to jump in.

Learning OCaml

…was difficult. There really needs to be a beginners guide that runs through creating a small project, that is kept up-to-date with modern practices. Even figuring out the right, and modern way to build projects was tricky. There are definitely some resources out there, but the learn page on ocaml.org doesn’t do a good job directing newbies on a path to get started. It’s just a list of resources and it’s not clear where to start and what to start with.

Resources

Real World OCaml. This is an excellent book and has probably been the biggest help in learning the concepts of the language. It’s a bit dated, especially around tooling and compilation, but otherwise very solid. The authors are currently working on the 2nd Edition which includes modern tooling.

#ocaml channel on the ReasonML Discord. This is an active and welcoming community, a great place to ask for help when you’re having troubles with concepts or tooling.

jbuilder Quickstart Guide. Learn how to compile your OCaml projects with jbuilder. Wish I had found this much earlier. Seems to be the best compilation story, at least until bsb-native starts working for OCaml > 4.02.3 and allowing for sub-modules in separate files (as of writing, there can be namespacing issues since each file represents its own project-global module, regardless of directory structure).

OCaml main forum. A good and active community, willing to help.

Using OPAM. The official documentation for the OCaml Pacakge Manger (OPAM). The documentation is clear and concise. Be sure to read up on opam switch ( --alias-of flag is good for separating OCaml version and libs between projects) and opam pin (pin a specific version of library so it doesn’t upgrade; can be used with local paths and remote git repos as well as version numbers).

vscode-reasonml. A great plugin that also supports OCaml for Visual Studio Code. If you’re not using Vim or Emacs, the editor/IDE story can be weak. This VSCode plugin has been mostly great for me.

The Good

I found that when I write something in OCaml, it generally works. The type system is very strong and basically forces my code to be correct. I’ve experienced this to some extent with Swift, but OCaml seems to be even stronger in this regard.

OCaml has just felt easy and natural for me. I’m not sure why exactly this is. The language feels relatively simple, with a few basic tools that can be arranged in many different ways. It didn’t take me long to feel like I understood most of what was going on. Reading the source of third-party libraries I was using came much easier than it has in other languages. With Haskell it felt like I needed to understand a lot of the functional programming concepts before I could really use the language. With OCaml, I feel like I am able to use it before I fully understand it, and that in the use I come to better understanding.

The package management story with opam is pretty great. It’s not yet at the ease of use of yarn , but it sure beats my experience with Haskell. Documentation is pretty clear and if there’s something wrong, opam ’s error messaging has been really helpful. When installing packages on Ubuntu, it even told me what libraries I needed to install via apt-get .

The Bad

The community is relatively small. This really hit me when I started looking for a library to work with PostgreSQL. Most of what’s out there is either outdated with modern OCaml/tooling or it’s not asynchronous. It turned out that what libraries were available and up-to-date decided what DB I was going to use (MariaDB). A forum discussion that sums this up. I ended up using ocaml-mariadb which has worked well, but it was nearly my only viable option at the time. Now, there is also ocaml-caqti which currently supports MariaDB, PostgreSQL, and SQLite3.

That said, with the popularity of Reason increasing comes an increased interest in OCaml. Also, many of the contributions to Reason also benefit OCaml.

Take Away

OCaml has been wonderful. I’ve come away from this project wanting to write everything I can in it. It seems to fit me better than object oriented programming ever did. Everything feels simple and transparent.

ReasonML

I knew that I wanted to build the web UI client of the URL shortener in React. I assumed I’d do this with TypeScript as I have with other projects. But as I got deeper into OCaml and its community, the more I learned about Reason. I wasn’t thrilled about the syntax changes from OCaml, but I had really warmed up to the language and wanted more.

Some have asked why I didn’t just use BuckleScript, allowing me to avoid Reason’s syntax changes. Reason is designed with React in mind and has great interop with it. I think those advantages outweigh the minor annoyance of the syntax changes.

What really sealed the deal for me was the account of Facebook’s experience rewriting 50% of Messenger in ReasonML. Way faster build times, 93% fewer bugs, improved iteration. This was enough to get my attention and suggest I should check it out.

Resources

In addition to the OCaml resources listed earlier, which mostly apply to Reason as well. These are pretty obvious, but I sometimes have found that I missed what many thought were obvious.

Try Reason. Does what it says on the tin. Write some Reason and it’ll show you the OCaml and JavaScript equivalents.

Official ReasonML Docs. They’re well written and cover the basics.

Official BuckleScript Docs. BuckleScript is basically straight OCaml with some syntax helpers for interop with JavaScript. Its compiler is used to compile both Reason and OCaml to JavaScript as well as to native binaries. These docs have some more detail on using non-Reason/OCaml JS libraries in Reason. This helps fill in gaps in the official Reason docs.

ReasonReact. If you’re going to be working with React in Reason, this is how to do it and it’s pretty great.

reasonml-community Github Org. There are a lot of helpful projects that live here. If you think something is missing, this is a decent place to start.

bs-webapi library. This has a lot of bindings to browser DOM and JS APIs that don’t come out-of-the-box with Reason.

ReasonML Editor Plugins.

Reason Package Index. Want to know if a JS library you want to use already has Reason bindings available? Search here. Also, most Reason compatible projects have bs- or reason- prefixes on GitHub.

Take Away

I found almost the same ease of use and “it compiles, it works” experience in Reason as I did with OCaml. The language and tooling are definitely experiencing some growing pains. As I was beginning with it, I’d get a lot of <unknown syntax error> messages from the compiler. But with some time I was able to learn the typical culprits.

I found the JS interop to be much easier to grasp than what I experienced with TypeScript. BuckleScript/Reason let you define just the types you want to use and takes your word for it. It makes it really easy to idiomatically solve most interop issues. The syntax felt a little weird at first, but after some time I’ve learned to appreciate it.

The team developing Reason seem to be pretty smart. If somethings not in the language yet, it’s because they’re trying to get it right the first time. The Promise story isn’t great yet, but it’s workable for now and I expect something much slicker to come out sooner than later.