Docs / BuckleScript / Introduction

The Reason Compiler

BuckleScript

BuckleScript isn't a new language. It simply takes OCaml, a fast, pragmatic and typed language, and makes it compile to clean, readable and performant JavaScript code. This allows the users to write in an industrial-strength language while using the existing, familiar JavaScript tools (npm/yarn, bundlers, minifiers, devtools, etc.) and accessing the vast ecosystem from both JavaScript and OCaml.

Thanks to its great interoperability with JS, BuckleScript can be seen as a "gradually" typed language, where you get to incrementally adopt it in your codebase file-by-file, while keeping the full benefits of a 100% sound and reliable type system, plus extra language features.

BuckleScript has built-in support for Reason, a partner project of ours, to provide an even more familiar experience to existing JS developers.

Why Compile to JavaScript?

JavaScript's ecosystem and community are very vibrant and active. The language itself is unparalleled in terms of adoption & distribution. Compiling to JavaScript is a therefore pragmatic choice (and sometimes, the only choice).

Additionally, the JavaScript VMs also received many engineering-years of fine-tuning and get faster all the time.

Why BuckleScript?

"Why not just use nothing but JavaScript then?"

Despite its clear advantages, JavaScript does present a few drawbacks. Here's how BuckleScript solves them.

Reliability

BuckleScript's type system alone offers three major benefits to make your programs more reliable:

  • Sound type system. "Sound" here means that the types guarantee that they are what they are, not just 90% of the time. Once a BuckleScript project compiles, there are no runtime type errors. *

  • Strong type inference. Almost the entirety of the language can be inferred. You don't have to tediously write all the types manually. Feel free to still type out some parts for readability!

  • Expressive type features. With well-thought-out features like variants, modules and even an opt-in object system, the types guide you through your iteration process and don't block you from expressing what you need.

The combination of these three highlights, along with other type system features, unlocks new workflow possibilities and is something existing gradual typing solutions for JavaScript don't offer.

* The exceptions are some deliberate, optional interoperability hooks.

High Quality Dead Code Elimination

The JavaScript ecosystem is very reliant on dependencies, and quite often many of them. Shipping the final product inevitably drags in a huge amount of code, lots of which the project doesn't actually use. These regions of dead code impact loading, parsing and interpretation speed. BuckleScript provides powerful dead code elimination at all levels:

  • Function and module level code elimination is facilitated by the well-engineered type system and purity analysis.

  • At the global level, BuckleScript generates code that are naturally friendly to dead code elimination done by bundling tools such as Rollup and Closure Compiler, after its own sophisticated elimination pass.

  • The same applies for BuckleScript's own tiny runtime (which is written in BuckleScript itself).

Compile-time Optimizations

JavaScript is a dynamic language; it takes a performance hit when the browser engine optimizes code at runtime. While some engines circumvent the problem to some extent by caching, this isn't always available nor predictable. The lack of a strong, built-in type system also limits the amount of optimizations possible. BuckleScript, on the other hand, is able to provide many optimizations during compilation time, way before the code's shipped to the browser. A widespread adage to write fast JavaScript code is to write as if there's a type system (in order to trigger JS engines' good optimization heuristics); BuckleScript gives you a real one and generates code that's friendly to optimizations by default.

Runs on Native Too!

BuckleScript is really just OCaml (apart from the JavaScript interop). OCaml can compile directly to machine code (aka fast, barebone assembly)*. When you're not targeting JavaScript, you can reuse the same code and target servers, desktops, mobile OSes, etc. This unlocks even better performance. For example, the traditional node.js application starts up in >100ms, while OCaml compiled to machine code starts in around 2ms.

* In fact, it even runs on microcontrollers with only a few KBs of memory.

How Is BuckleScript Different?

Nowadays, there are many languages out there that compile to JavaScript. BuckleScript distinguishes itself from the alternatives in a few crucial ways.

Fast Iteration Loop

BuckleScript's build time is one or two orders of magnitude faster than alternatives. In its watcher mode, the build system usually finishes before you switch screen from the editor to the terminal tab (two digits of milliseconds). A fast iteration cycle reduces need of keeping one's mental state around longer; this in turns allows one to stay in the flow longer and more often.

Readable Output & Great Interop

Unreadable JavaScript code generated from other compiled-to-js languages makes it so that it could be, practically speaking:

  • Hard to debug (cryptic stack trace, mangled variable names)

  • Hard to learn from (non-straightforward mapping of concepts from one language to another)

  • Hard to profile for performance (unclear what runtime performance cost there is)

  • Hard to integrate with existing hand-written JS code

BuckleScript's JS output is very readable. This is especially important while learning, where users might want to understand how the code's compiled, and to audit for bugs.

This characteristic, combined with a fully-featured JS interop system, allows BuckleScript code to be inserted into an existing JavaScript codebase almost unnoticed.

Tiny JS Output

A Hello world BuckleScript program generates 20 bytes JS code. Additionally, the standard library pieces you require in are only include when needed.

Preservation of Code Structure

BuckleScript maps one OCaml/Reason file to one JavaScript file. This eases the integration of existing tools such as bundlers and test runners. You can even start writing a single file without much change your build setup. Each file's code structure is approximately preserved, too.