
BuckleScript: An OCaml to JavaScript compiler - jasim
http://yawar.blogspot.com/2017/01/bucklescript-significant-new-ocaml-to.html
======
hongbo_zhang
Hi, the main author of BuckleScript here, below is a simple comparison with
TypeScript (it may be biased):

\- Pros of TypeScript

    
    
      - Designed for JS, easy inter-operate with JS
      - A much larger community
      - Nice integration with VSCode
    

\- Cons of TypeScript

    
    
      - Compiler slow (not scalable for FB/Google scale)
      - Verbose, very limited type inference
      - Start up time slow (very hard to build traditional build tools)
      - Types are only used for tooling - soundness is not the design goal, not very reliable
      - Only JS backend, no code optimizations
    

\- Pros of BuckleScript

    
    
      - No legacy of JS (clean slate) while great story in inter-operation
      - Compiles much faster, scales much better
        10% perf is a nice enhancement, 10x faster is an invaluable feature
      - An arguably much better language
      - Sound type system, global type inference
      - Types are used in code optimization, optimizing compiler
      - Javascript backend + Native backend for X86, ARM
    

\- Cons of BuckleScript

    
    
      - Smaller community
      - Learning curve is higher compared with TypeScript

~~~
vvanders
> Compiler slow (not scalable for FB/Google scale)

I don't have any dog in this fight(I write Java/Native mostly) however I'd
don't quite follow what you mean by FB/Google scale. I would consider VSCode a
pretty complex piece of software and my understand is it written in TS.

Have any concrete numbers you can share that makes it not meet your bar?

~~~
djur
Looks like VS Code is about 332 thousand lines of code. Facebook and Google's
codebases are measured in the tens of millions of lines of code.

~~~
vvanders
A 10 million line javascript file must take a heck of a long time to load in
the browser :).

~~~
yawaramin
Modern JavaScript is developed in separate modules (i.e. files).

~~~
mort96
Developed, yes, but in general, your build system bundles it all up into one
javascript file which is sent to the browser. Even if you don't bundle the
javascript, all the code has to be sent to the browser anyways, just over
multiple HTTP requests instead of one.

~~~
Can_Not
Except for small or basic apps, your build system is not suppose to build to
only one file. It's suppose to build to a main file and separated module
files. The module loader will pull in modules only when needed.

------
jasim
There is a specific niche that BuckleScript and Reason can fill, and that is
the sort of front-end projects that use
React+Redux+ImmutableJS+TypeScript/Flow.

Instead of bolting on typing into Javascript (Flow and TS both do a good job
of it btw), you could simply use a language that was designed with it. There
is a huge difference in native adoption of such a core language feature vs
using a transpiler that ultimately leak Javascript semantics.

If you're using immutable values through ImmutableJS-like libraries, or by
just being careful to not mutate stuff - then you should try working in a
language that has immutability baked in. Having confidence that you've been
careful to not mutate anything in your code vs using a language that simply
disallows it in the normal course of programming brings a tangible difference.

You can call BuckleScript/Reason as just a better version of Javascript with
all the good things baked in. But it definitely is much more - you get to use
Variants (Sum/Union types in other parlance), and more importantly, writing
value-based immutable code (instead of reference based mutation) lets you rely
on equational reasoning to understand your code. Your functions are pure and
can be seen as though they are equations, and your whole program is a
composition of these equations.

A good thing about BuckleScript/Reason vs transpiled languages like
CoffeeScript is that we're fully free from Javascript's semantics. You are
solidly inside OCaml, and Javascript simply doesn't leak through. You don't
have to worry about JS scoping rules or how `this` works, or how ES6 classes
are ultimately prototype-based. Except for a well-defined foreign-function
interface that lets you use all of npm libraries or your existing JS code,
once you are in the typed immutable world of OCaml, then that's all you need
to think about.

All of this applies not just to BuckleScript, but any Typed FP that runs on
the browser - Elm, PureScript, and I believe Scala.js as well.

But one big advantage (or disadvantage, depending upon how you look at it) of
OCaml/Reason/BuckleScript is that it allows mutation if you need it. You can
start out writing purely imperative code just as you would in Javascript, and
slowly adopt immutable programming as you get comfortable with it. You can
also rely on mutation when you need to optimize specific parts of your code.

~~~
jahewson
TypeScript is far more powerful than you think. Between const, readonly
properties, Readonly<T>, ReadonlyArray<T>, readonly indexers, and type safe
object spread {...} via keyof T, TypeScript provides the tools to write
immutable code, fully checked by the compiler, without paying runtime costs.

It also has tagged unions, product, and sum types, nullability, and extensive
capabilities for typing 'this'. So I'm left to ask what exactly _is_ the
unique advantage of writing JS in OCaml? Because it's not immutability.
Remember too, that OCaml has plenty of warts.

~~~
rlander
> what exactly is the unique advantage of writing JS in OCaml?

* OCaml's type system is sound, Typescript’s isn’t.

* OCaml is an expression-based functional language, with first class currying and partial application, TS is an OO language.

* OCaml has pattern matching and exhaustive checks, TS doesn't.

* OCaml leverages true immutable structures, TS doesn’t.

* OCaml has a super fast compiler, TS doesn't.

~~~
parley
I use TS myself. It helped make front end development OK for me again. But
man, one really misses some things.

I recently picked it up again and struggled to remember how to properly do
pattern matching and exhaustive checks, until I realized that there were more
hoops to jump through than at a circus convention. A friend ultimately pointed
me to [https://pattern-matching-with-typescript.alabor.me/](https://pattern-
matching-with-typescript.alabor.me/) but that made me weep.

Then I remembered how I hadn't been able to figure out how to use object
spread to perform variable assignments of _all_ members from an object
returned from a function, with compiler verification that no object members
had been forgotten. That may be possible now, I'm not sure. I don't think it
was possible last I battled with it.

Don't get me started on the ergonomics of immutables. Again, this may have
improved, and no one would be happier about that than I.

But all in all I feel that TS is not what I'd _like_ to use for front end
development. However, it's where the community and development effort is at,
and I feel like I could never convince my colleagues to go with
BS/PS/Reason/anything else. I haven't even been able to push them from JS to
TS yet.

If someone wants to show me the error of my ways, I'd love it, since I'll
probably be using TS for some time to come.

------
lobster_johnson
Looking forward to giving this a tryout. We have a server-side Node.js project
where performance is terrible, and memory leaks happen that are almost
impossible to track down; I need to rewrite it, and I was thinking Go, but
this project also has a bunch of code that is also used by JS clients; so
something JS-compatible would be better.

What's BS's story when it comes to interfacing with async code? In particular,
all my Node.js code these days is promise-based (I promisify all callback-type
functions, except for event-based ones that I try to wrap) using async/await.
Do you still have to use promise calls in BS? Or are compatibility operators
offered for chaining async calls more elegantly?

What about async in general? OCaml has LWT -- can you use that with BS and
still run on Node.js or in the browser?

~~~
yawaramin
Right now we are using bindings to JS promises:
[https://www.npmjs.com/package/bs-promise](https://www.npmjs.com/package/bs-
promise)

We also have Lwt compiled with BuckleScript: [https://github.com/mzp/bs-
lwt](https://github.com/mzp/bs-lwt)

But, bs-lwt right now is not compatible with promises--it is a straight port
of the OCaml implementation. I'm told that someone is working on an Lwt backed
by JS promises for BuckleScript.

------
seanwilson
Are there examples anywhere showing if this is productive to use with a UI
framework (e.g. Angular, Vue, React)? What are the thoughts of people that
have personally tried it?

OCaml is one of my favourite languages but it feels like it would be going
against the grain just too much right now compared to using something like
TypeScript.

~~~
ch4s3
Here's an example with The Elm Architecture [1]

A long discussion about using it[2]

And a react example[3]

[1][https://github.com/OvermindDL1/bucklescript-
tea](https://github.com/OvermindDL1/bucklescript-tea)

[2][https://elixirforum.com/t/bucklescript/1579](https://elixirforum.com/t/bucklescript/1579)

[3][https://github.com/joshaber/bucklescript-react-
test](https://github.com/joshaber/bucklescript-react-test)

------
k__
Pretty cool project.

OCaml -> AST -> JavaScript

It even solves known expressions at compile time.

~~~
mrkgnao
And compiles are really, really fast. (It's an OCaml thing, I gather.)

~~~
k__
Seemingly. I've never used such a fast compiler

------
lauritzsh
How could I incrementally migrate a TypeScript React project to a BuckleScript
(maybe using Reason bindings) React project using webpack? Could I easily add
the bs-loader and just import BuckleScript components into my TypeScript
components? Can BuckleScript share types with TypeScript or would that require
an external TypeScript type definition file?

It looks very interesting and I am really missing some of the features when
working with TypeScript. It could be useful if I could slowly transform the
codebase from TypeScript to BuckleScript.

~~~
yawaramin
Reason-React has support for transitioning from JS:
[https://reasonml.github.io/reason-react/index.html#reason-
re...](https://reasonml.github.io/reason-react/index.html#reason-react-
interop-with-existing-javascript-components)

In your case you need to make sure you're binding against the JavaScript
output, not the TypeScript input. Drop by the #react channel in
discord.gg/reasonml to chat.

------
cookiecaper
JavaScript is becoming a must-have compilation target. I hope that we can cut
out the middleman soon via WebAssembly.

In particular, I would like to see browser vendors distribute a small number
of wasm-compiled VMs that function as first-class JS alternatives. This would
provide some sense of stability and blessing, and prevent a delay in page load
time caused by the download of an alternate client implementation.

I started to seriously undertake a Dart-based project in the last couple of
weeks but eventually decided that the ecosystem is too immature to make it
worthwhile. I'm going to try Scala.js for a while and if that doesn't pan out
I'll probably be forced into TypeScript. I had previously also tried
Go/GopherJs.

~~~
fulafel
WebAssembly is quite a small change from asm.js - it's mainly a different
transfer format and a parsing speedup. It's targeted towards non-GC languages
like C/C++ and Rust - for managed languages, Javascript is the only sane
compile target.

~~~
cookiecaper
Scripts for GC languages wouldn't be compiled directly for WebAssembly --
their runtimes would. That's why I suggest browser vendors bless a few
specific alternative runtimes and distribute them with the browser. For
example, PyPy would be compiled for WebAssembly, and then Python could be
executed _directly_ as a client-side web language. Same with Dart and other
languages that depend on a VM to do the heavy lifting.

~~~
mraleph
> Scripts for GC languages wouldn't be compiled directly for WebAssembly --
> their runtimes would

It is not a walk in the park to compile runtime into WebAssembly (unless you
are just compiling an interpreter).

Performant runtimes usually include machine specific backends and/or hand
written assembly code. You'll need to port that all to target WASM too.

~~~
cookiecaper
Yeah, though I haven't worked on that directly, I'm sure it's not trivial, but
I think there is sufficient demand. Check out
[http://pypyjs.org](http://pypyjs.org) , which uses emscripten to compile to
asm.js and then bolts on a custom JIT that emits asm.js, for one example of a
project that is already trying something like this.

------
Sophistifunk
FWIW, if you're looking for compilation speed I suggest HaXe. Its syntax is
very familiar to TS / AS3 / Flow developers, and the compiler is very fast.

....because it's written in OCaml ;-)

------
WaxProlix
It'd be cool to have example input and output code somewhere.

~~~
simiano
OCaml => Js: [https://bucklescript.github.io/bucklescript-
playground/index...](https://bucklescript.github.io/bucklescript-
playground/index.html)

Reason => OCaml => Js:
[https://reasonml.github.io/try/](https://reasonml.github.io/try/)

------
iamleppert
He honestly lost me when he said he couldn't understand how this works in
JavaScript.

Also, what is meant by Google scale? Do real people really go around choosing
a language for a project thinking "gee, if I ever get to the size of Google
I'll sure be glad I choose BuckleScript!"

Also, what is meant by an optimizing compiler? The optimizations are done by
V8. Any possible pure language optimization you could be doing likely isn't
worth it at all.

It's also not correct to say there's no JS legacy. There will be a legacy the
first time you need to look at any of the generated JavaScript code.

Next, who wants to build a code base using a language that's hard to learn and
come up to speed in? If the entire point is net productivity, you better be
able to offset all that upfront costs by demonstrative gains elsewhere. Of
course, no one will ever measure stuff like it and it will be very subjective
and vary per person.

~~~
Sawamara
You should have just seen how the compiler works. There are examples on the
side.

For example: an {a,b} struct is compiled down to a [a,b] array in JS. (by the
compiler).

This is an optimization. V8 can only optimize the code that it is given (and
sometimes, deoptimize). The better your code is, the more easier V8 can
optimize it.

------
iainmerrick
Is there a IDE or editor plugin with autocomplete for Ocaml and/or Reason?

~~~
yawaramin
The recommended plugin is
[https://marketplace.visualstudio.com/items?itemName=freebroc...](https://marketplace.visualstudio.com/items?itemName=freebroccolo.reasonml)

~~~
iainmerrick
Is it fast? Visual Studio for Mac can't handle text input in real time when
I'm editing C# code.

~~~
kcorbitt
It is extremely fast. I think you'll be very pleasantly surprised by the
runtime speed of OCaml if you're coming from interpreted languages, and by the
build/tooling speed if you're coming from compiled languages. It really does
hit a nice little sweet spot. :)

~~~
iainmerrick
Sounds great, I'll give it a try!

------
OtterCoder
What server implementation is the author referring to?

~~~
jasim
BuckleScript is an OCaml to JS compiler. So you can convert your code into JS
and run it from Node. Or, you can simply compile it into native OCaml binary
and run it just like you run a C or GoLang binary.

The choice would depend on whether you use libraries from the npm ecosystem,
or from the OCaml ecosystem.

~~~
OtterCoder
Thank you. Is there a particular native OCaml server implementation that is
considered best, or at least most popular?

~~~
yawaramin
Yes, Cohttp is from the folks who developed the Mirage unikernel:
[https://github.com/mirage/ocaml-cohttp](https://github.com/mirage/ocaml-
cohttp) . They make lightweight stuff that runs blazing fast.

~~~
testcross
But cohttp is slow. See [https://github.com/mirage/ocaml-
cohttp/issues/328](https://github.com/mirage/ocaml-cohttp/issues/328) and
[https://github.com/inhabitedtype/httpaf](https://github.com/inhabitedtype/httpaf)

------
etplayer
I've been rather worried about the state of parallelism or concurrency in
Ocaml, two conecpts which I have little understanding of, but the rumours of
the insufficiency in support in the Ocaml compiler/interpreter have offput me
from using Ocaml.

Would someone be so kind as to shed some light on this issue for me? Does it
affect, say, a web server implemented with OCaml? What about a program in
which some background process is used to prepare something simultaneously to
the main process, like loading assets for a game?

~~~
yawaramin
It's not a problem in practice. OCaml has great concurrency support, which is
what you need for applications that do IO, like servers. For CPU-bound
applications like number-crunching, you take advantage of existing bindings to
C or Fortran libraries. For a thorough discussion, see
[https://discuss.ocaml.org/t/7-years-after-is-ocaml-
suitable-...](https://discuss.ocaml.org/t/7-years-after-is-ocaml-suitable-to-
write-networking-servers/639)

Also remember that with BuckleScript, you're deploying to JavaScript, which is
single-threaded by definition. OCaml's runtime model (modular, single-
threaded, strictly-evaluated) maps beautifully to JavaScript.

