Hacker News new | past | comments | ask | show | jobs | submit login
BuckleScript: write JavaScript faster, safer and smaller (bloomberg.github.io)
214 points by _qc3o on Jan 21, 2017 | hide | past | favorite | 90 comments



From Why BuckleScript[1]

Large JS output even for a simple program

"In BuckleScript, a Hello world program generates 20 bytes JS code instead of 50K bytes. This is due to the fact that ... all BuckleScript’s runtime is written in OCaml itself so that these runtime libraries are only needed when user actually call it."

What happens when you go beyond Hello World though? Surely there's some overhead vs. plain JS. For example, Hello World in Scala.js is tiny, but once you touch, say, the Scala collections library, then file size increases significantly. In the end, once you go beyond trivial applications, there's a 150KB baseline tax to pay for using the full power of Scala in the browser.

If BuckleScript provides an OCaml-like language with file sizes comparable to plain JS that is both compelling and impressive.

[1] http://bloomberg.github.io/bucklescript/Manual.html#_problem...


Unlike scalajs, bucklescript maps one OCaml module to one Js module, it is like coffecscript/typescript compilation model, there is no tax on the code size. You can write very clean code, here is an example of avl tree in ocaml compiled into JS(http://bloomberg.github.io/bucklescript/js-demo/#Balanced_tr...)


I like that BuckleScript has chosen the module-by-module design, if only to explore more design space than what was previously done in Scala.js and ClojureScript.

I do not think this is what makes the 150 KB tax of Scala.js, though. The dead code elimination of Scala.js is really good, plus we combine it with Closure as well. But it faces a very difficult challenge: the entanglement of the Scala standard library. The collection library was designed when the JVM was the only target, and decoupling within the collections library was definitely not in the requirement set. This is why, once you touch the Scala collections library in Scala.js, you receive 150 KB worth of (non-gzipped) code. There is currently a large-scale effort to redesign the collections library for Scala 2.13 [1], with, among others, a desire to reduce inter-dependencies (while keeping most of user-level compatibility). This should hopefully significantly improve the situation of Scala.js regarding code size for smallish applications, as you will only pay for the collections you actually use; not the entire set of collections once you require one of them.

[1] https://github.com/scala/collection-strawman


.NET was (past tense) also a target of Scala. Also didn't work out.

Java is the greatest and worst part of Scala.

Without Java, the language would be far, far less popular than Haskell. With Java, it has a lot of forced compatibilities, e.g. null.


> Also didn't work out.

Are you implying that Scala.js didn't work out? That would be denying the huge success it has within the Scala community. It is notably supported by most major Scala libraries. To back this by data, it appears 5th in a ranking of Maven artifacts based on the number of artifacts depending on it [1]. Above it are Scala itself, Specs2, Akka and scoverage.

Scala/.NET didn't work out because we never managed to reconcile the type system of Scala with that of the CLR. For JavaScript, we did manage that, and pretty easily: it's much easier to erase all types than converting from an advanced type system to another advanced type system.

[1] https://scaladex.scala-lang.org/search?q=&page=1&sort=depend...


> Are you implying that Scala.js didn't work out?

Er, definitely not. Bad mis-statement on my part. Should have said "also had challenges." And you're correct about type erasure.

Scala/Scala.js/SBT are my tools of choice for web apps.

I gave a talk "Scale your code with Scala.js" at Fluent last year https://conferences.oreilly.com/fluent/fl-ca-2016/public/sch...


Cool!

I had completely misunderstood your earlier comment ;)


^ that's completely ridiculous.

Sounds like Reason can run the show as well (OCaml's crufty syntax has always bothered the SML'er in me), going to give this a look.


I've actually been playing around with building a Screeps AI in various languages, and found the sizes the be interesting. It's not really apples-to-apples since all of the implementations aren't near the same, but I think it provides a bit of context in terms of order of magnitude. Everything using the closure compiler with advanced compilation (for screeps I don't usually minify, but I think the dead-code elimination is important when comparing sizes):

Bucklescript: 16K

Haste (Haskell): 36K

ClojureScript: 144K

It's worth noting the Clojue version is the furthest along, and the Bucklescript version is the least, but all do some ffi and list manipulation. I did some simple tests with ghcjs, but it was producing files over 1m even for simple "Hello World" type code (I think because it includes the entire Haskell threaded runtime), so I didn't go very far with it.

[1] https://screeps.com/


I wrote up a non-technical post about my experience with BuckleScript http://yawar.blogspot.ca/2017/01/bucklescript-significant-ne...


"BuckleScript is one of the very few compilers which compiles an existing typed language to readable JavaScript."

Elm (type inference) is sort of. While there is a lot of OS type projects used, having bloomberg use it is a plus. Interesting that FB have build system ^Reason^ (build system) doing something similar (OCaml backend->JS) ~ http://facebook.github.io/reason/


Reason is an alternative, JavaScript-like, syntax for the OCaml language. Same language, different syntax! It's crazy cool. BuckleScript is an OCaml -> ES5 compiler, so it actually compiles Reason to ES5 just fine.

Bloomberg and Facebook are two big users of BuckleScript. FB have a couple of folks writing significant stuff in Reason, compiling with BuckleScript, and deploying to user-facing web properties.


What is the difference between reason and bucklescript?


Reason is a language, bucklescript is an alternative backend for ocaml which generates JavaScript. You write reason, you don't write bucklescript


okay, then what's the difference between ocaml and reason? :)


I spoke quite a bit about ReasonML during 2016, hopefully this should cover a lot of what Reason is, how it relates to OCaml, and how it's different: https://www.youtube.com/watch?v=8LCmLQ1-YqQ

Happy to answer any questions I didn't get to in the talk as well!


Reason is a js like syntax for ocaml, semantically they're identical. Similar to coffeescript and JavaScript, except ocaml has really good support for this kind of stuff


So what you're saying is I could write a JavaScript-esque program and compile it to OCaml using Reason, and then compile it to true JavaScript using Bucklescript?


Yes exactly, although you can avoid the middle step and go straight from reason to js because of the way bucklescript plugs into the ocaml compiler


Why would one prefer reason to ocaml? (Honest question)


You might find this page from the Reason website useful: https://facebook.github.io/reason/mlCompared.html

It replaces a lot of the idiosyncratic bits of OCaml's syntax with things that feel more appropriate if you're comfortable with other modern languages.

A quick example is tuple types. OCaml declares tuple types as int * int, which makes total sense if you know that tuples are a product type. However, you actually create tuples using commas. Reason throws away the connection to theory and uses the same syntax for types (int, int) and creating values of that type (3, 7).


Reason's syntax is a little cleaner and it's easier to get up and running with.


It is much more like JavaScript than ocaml and so you get a familiar language but one which benefits from all the cool things that ocaml can do.


Another n00b question: What are "all the cool things that ocaml can do"?

I only saw rather simple typing examples, which didn't seem much different from what TypeScript can do.

On the other hand I read often that ocaml doesn't have runtime errors, which I certainly get with TypeScript.


This is a good overview I think, although it doesn't get into specifics: https://realworldocaml.org/v1/en/html/prologue.html#why-ocam...

Of course the rest of the book is a great read too if you're so inclined.


OCaml does have exceptions at runtime (eg if you try to take the maximum of an empty list), but generally your programmes won't have type errors at runtime.



DDC is an alternative to the dart2js compiler. It supports "strong mode" which provides better compile time checking, and produces more idiomatic Javascript.

Dart has a large team behind it, an awesome package management infrastructure, a "Dart native" angular 2 library.


Dart is just another junk food programming language in the same vein as Java (with some of the same people involved). If you really want to learn what a "strong mode" is, learn OCaml. Good job Bloomberg for Bucklescript.


Kotlin can do that too: https://kotlinlang.org/


Kotlin is slow though. To compile that is.


I'm still waiting on full JSX and React for BuckleScript.


JSX is available in Reason out of the box: https://facebook.github.io/reason/#diving-deeper-jsx


Oh snap, didn't know this was supported yet.

I think the tooling isn't available for transforming Reason => JSX in a consistent development environment from what I gather, but I haven't paid attention for a few months.


"Interesting that FB have build system ^Reason^ (build system)"

One reason to define a tool and use language clearly, I interpreted, 'Build Systems Rapidly' as a ^build system^ where in reality Reason is a ^systems language^ to build things quickly.

Some of the tools are fantastic. I would love to have REFMT "dynamically as the window resizes in order to make optimal use of screen real estate while still abiding perfectly by the formatting rules" built into language editors to aid development.


didn't gwt do this 5 years ago


If we're just talking about compiling a typed language to javascript, gwt did it 12 years ago.


This looks pretty interesting. It makes me want to learn OCaml. :-P

I've been using Coffeescript in projects for about two years. I know people have raised issues with it vs ES6, but I still really like Coffeescript. I feels more natural with my Scala/Ruby background and it outputs into Javascript in ways that (mostly) make sense and are predictable.


> This looks pretty interesting. It makes me want to learn OCaml. :-P

You can learn F# with dotnet core, which has a better ecosystem for web development. OCaml is quite poor in that domain.


Have you heard of Ocsigen/Eliom? It's a complete OCaml system for web development.


Are you speaking from experience? If so, do you have any or know of any resources I could look at? Also, what type of backend are you using with F# and dotnet core?


If you want the BuckleScript equivalent for F#, that would be Fable.

https://github.com/fable-compiler/Fable/blob/master/README.m...

There's a newer branch called Fable Arch which I believe is focused on promoting the Elm Architecture for structuring web apps:

https://github.com/fable-compiler/fable-arch

FunScript is another alternative:

http://funscript.info/


Elm Architecture for F# is Elmish: https://github.com/fable-compiler/fable-elmish


I do not think that Arch is a newer branch but a framework that uses the standard fable compiler.


One popular web server is https://suave.io/ You can use http://fable.io/ for f# -> js transpilation. One big advantage is that you are able to use ts definitions for foreign js libraries. The js is generated by babel which is also nice.

There are several active projects and bindings that you can take a look at there: https://github.com/fable-compiler


What kind of data store do you use with F#? Some sort of SQL variant?


https://blog.geist.no/suave-io-introduction-and-example-part...

"Database access from a Suave.IO app isn't any different from database access from any other .NET / F# app you write."

Any way you like, really.


I was using LiveScript before ES2015 and syntax-wise it felt a step backwards to go back to JavaScript.


While CoffeeScript has some useful additions, it also has some that were not good ideas. When the language helps communicating an idea more clearly and concisely, it's fine.

But removing delimiters in my opinion does not help readability. Most time is spent reading, not writing. It's similar to claiming more efficiency by removing lanes and signaling from a highway...

Then, whitespace has more meaning than it should in CoffeeScript, and inconsistent indentation is possible, whereas in Python/Ruby it is not without an error. Therefore a formatter can break your code.

Snippets/templates/autocompletion can save the work of typing delimiters. Delimiters can be enforced with CoffeeLint, but then that's optional, it is more work, and it is a friction point with people that do not see value in formatting.

CoffeeScript does not have a lot of tooling around it, and the community has moved on.

There are documentation tools like docco and codo, but there is no tooling that makes it as well integrated with the language as JavaScript + JSDoc, where there is tooling that can verify types, function parameters and return types giving you a lot of static analysis power.

Then, the entire point of a scripting language is not having to compile. With CoffeeScript you lose that advantage to some extent, and the compiler does not filter many errors, making it a bad tradeoff.


I found a page explaining the differences between bucklescript and js_of_ocaml : https://github.com/bloomberg/bucklescript/wiki/Differences-f...


Built in npm support is huge for me. I use Scalajs full time and one of my biggest issues is having to use SBT and webjars. I also end up using some mashup of npm modules as well later on in the build which complicates it even more. Plus the speed of the compiler becomes a massive issue, especially during unit tests.

By turning it into a more readable Js, I wonder if the debugger would play more nicely too. I've noticed in Scalajs the debugger randomly misses breakpoints or moves to the wrong line sometimes. Anyway not a rant on Scalajs, I do like it but there's definitely some bottlenecks for me.


I use Scala.js too. I haven't been able to set up a working sourcemap with our workflow of Scala.js -> Webpack -> JS. It's difficult to debug the mangled JS output but I can just about do it--barely.

By contrast, reading BuckleScript's output JS is a breeze. No name mangling, idiomatic style, sensible code flow.


npm support is now available through https://scalacenter.github.io/scalajs-bundler/

I agree with your analysis on breakpoints and step-by-step in Scala.js. Usually it's a good idea to disable the Scala.js optimizer if you're doing that, using:

    scalaJSOptimizerOptions ~= { _.withDisableOptimizer(true) }
But it's still not 100% perfect. I am toying with the idea that we should have a mode of the optimizer that tries to optimize for debuggability with step-by-step and breakpoints. This would try to arrange the generated source in closer relation with the source code (like, 1 line = 1 line). All of this is trying to work around limitations of source maps, and also of browsers' support for source maps. It would be so much easier if source maps were a little bit more powerful in what they can express (e.g., indicating what a "step" should be in the code).


This bundler looks awesome sjrd, will give it a try soon. My other option I was going to suggest to other is to take a look at the sbt-web project. But keeping things compatible and seamless with npm I think is important since it's ubiquitous with front end devs


This looks really interesting. Does this implement the entire OCaml language? I found a section of the docs marked 'semantic differences', but it seems... suspiciously small...

This would allow me to write GWT-style client/server apps where both ends are written in the same language with a set of common library code compiled for both, right? What's the library support like? Don't suppose there's any ELM-style DOM diffing support, is there?


Yes, we implement the whole language, except a very few places that is hard to due to the limitation of JS runtime.

Exactly, client side/ server side in a single language. People are working on porting the Elm architecture to BuckleScript, they will be coming soon


I think BuckleScript is going to blow up one of these days and become hugely popular.


I am waiting for the next Windows release that would allow one to use the bash subsystem from native programs so I can use Reason/BuckleScript with Vscode. OCaml/Windows integration isn't the greatest.


BuckleScript itself works fine on Windows. Installs in an npm project with `npm install bs-platform` and good to go from there. Try it out.


I can run BuckleScript under Windows/Bash. However as I understand it one needs things like merlin working to get a decent IDE experience.


There's an OCaml VSCode extension powered by merlin.

https://marketplace.visualstudio.com/items?itemName=hackwaly...


you should be able to use linux VSCode with a win32 X server like mobaxterm right now.


I am trying to wrap my brain around this one. Do you mean I can run the VSCode under the bash subsystem and connect to an X server? Good to know this is workable.


Exactly like that. Run the elf binary, connect to PE X server.


Learn a language to program another language. Find the issue. (I did not say that BuckleScript is bad, i have only a problem with all kind of these tools)


How is there an issue at all? Most languages people use compile down to something else. You don't need to know assembly to write Ruby.


I cannot speak for the parent, but the difference for me is this: Matz wrote Ruby, for instance, because he felt that Perl lacked some features/capabilities that he wanted. This is good, and from this perspective, I have no complaints. Now, if Matz wrote Ruby to compile down to Perl, then it becomes an issue. At that point, Ruby wouldn't really be a new language, but just a Bandaid to hide the weaknesses and blemishes of Perl, for instance. Fundamentally, I would still be writing Perl, and that, for me, is where it becomes an issue, because I really haven't solved anything. I've just disguised it.

My opinion on the matter is that Javascript sucks. We all know it sucks too, which is why things like Elm, CoffeeScript, ClojureScript, TypeScript, BuckleScript, Scala.js, etc. exist. We want to fix Javascript because we understand that it sucks; therefore, we tend to embrace these various attempts to make it less stupid. The solution to the problem, in my mind, isn't yet another Javascript transpiler though, but rather native support for superior languages.

I'd like to see an API built into browsers to make them language agnostic. I think that would go a long way towards making web programming less lame. It may also give us a chance to clean up the DOM.


Our bearded forefathers wrote C because assembly had some issues. They still compile to assembly.

Haskell compiled to C for the longest time (and quite a few other compilers use that trick). That didn't make Haskell a bandaid around C, did it?


WebAssembly is a work in progress. I think they had their first release some time ago, but I don't know how long it will be before it's safe for languages to target it for compilation.


Language-agnostic browsers are the perfect solution. Transpilers are a good-enough solution. And as they say, 'The perfect is the enemy of the good enough'.


There's no issue, as long as you know both languages. Which you should anyway - both JS and OCaml are interesting in their own right.


The issue is having to go through that other language. So your problem is JavaScript's monopoly, right?


BuckleScript looks great but I've never found any project using it - maybe Reason will be the tooling to make it mainstream?


Projects in Bucklescript do exist, here's one that shows how easy it is to integrate with JS - https://github.com/paulhoughton/montecarlo/

Or lots of examples here - https://github.com/OvermindDL1/bucklescript-testing


http://www.purescript.org - PureScript a small Haskell like programming language compiled to JavaScript (and C++)


Ooh, I wonder this could lead to the Haxe compiler running in JS.


For those who need babel source maps there's Fable, it uses F# and Babel to generate javascript, OCaml and F# are remarkably similar.


Well, Rust can compile to asm.js or wasm, too.

Might still be rough around the edges, but it will improve soon enough.


Rust compilation is slow, and the compilation target opaque.

BuckleScript is fast, and compiles to readable JavaScript.

Please don’t just go spouting that Rust is the answer to everything; it’s not. It’s also not always relevant in a discussion.

(I say this as someone who is lauding Rust at every turn and who has been using it seriously for years and who is looking forward to a solid wasm target and ecosystem.)


Sorry, my post maybe came off as more negative than I intended, but the downvotes seem a bit harsh.

To me, in the WebASM world, transpiling to JavaScript is starting to seem a little old-fashioned. Readable JavaScript is not as important as long as we have source maps, which WebASM was designed to take into account. Presumably Rust (or Ocaml) compiled to WebASM will also have substantially improved performance compared to transpiled JavaScript, which IMO is worth the longer compile cycles.

I think Rust is relevant because Ocaml and Rust share a lot of heritage (indeed, I think Ocaml was one of the most influential languages in the Rust design), so I think that makes it relevant in this discussion.


The problem with wasm right now is that it gives you very little in terms of interoperability with JavaScript. You simply cannot manipulate JS objects (including DOM objects) from wasm.

Compiling a language like BuckleScript to wasm is therefore not possible at the moment, simply because the interop features of BuckleScript cannot be encoded in wasm.

Besides, there are other difficulties: wasm doesn't have any kind of managed heap at the moment, which means that compiling a managed language to wasm requires to embed an entire GC in your production code! It's much better to take advantage of all the VM features offered to JS, like a GC, when compiling a managed language to the Web platform.


> To me, in the WebASM world, transpiling to JavaScript is starting to seem a little old-fashioned.

It's behind a flag in a couple browsers. Even with a flag, if you're going to actually do anything with the DOM, you're going to be shipping a js support library and calling into it. Using something that works now has value, particularly when you could get wasm in the future off the same code if anybody writes an OCaml wasm backend.

> Presumably Rust (or Ocaml) compiled to WebASM will also have substantially improved performance compared to transpiled JavaScript

Bucklescript adds asm.js type hints (but not the use strings) to the js output and does a fair number of optimization passes. Aside from reducing parse time, going from its current output to wasm is unlikely to pick up much in the way of perf. I'm actually not even convinced that wasm is going to be that much faster for load+parse over an optimized js payload because asm.js/wasm have to ship the runtime (allocator, stdlib, etc) while js just has to ship the code.

> Readable JavaScript is not as important as long as we have source maps

I write Clojurescript full time, which has source map support. I completely disagree with this statement.

> Ocaml and Rust share a lot of heritage

Rust was bootstrapped off an OCaml compiler. I like Rust and advocate for it but bringing up another compile-to-js language, particularly one as nascent as Rust doesn't really have anything to do with the merits or drawbacks of Bucklescript.


> BuckleScript is fast, and compiles to readable JavaScript.

What is the obsession with having readable JavaScript? In the debugger (devtools) I don't want to step through the low-level assembler instructions (JavaScript), I want to remain in the high-level language where reasoning about the behaviour is actually possible. We have sourcemaps for a reason, make use of it!


Readable JavaScript or not might not make a difference in the long run, but it sure helps adoption when a single person on a team can start using Bucklescript without having the pure JavaScripters hate her for it (or even having to know she's not writing her js by hand).


Sourcemaps aren't always possible with some workflows e.g. transpiling then bundling. Plus, readable JS is a huge productivity boost because it lets people transition away from JS into OCaml while still understanding their JS output every step of the way.


I don't understand why your comment was downvoted - it does seem to be relevant to the topic of compiling a typed language to Javascript.


If that comment were relevant, then so would 100 other similar comments mentioning another typed language that compiles to JS.

Do you really want to drown the interesting HN comments in a heap of "hey, my favorite language also compiles to JS" kind of comments?


Advantages?


Could somebody compile Flow to JavaScript?


The flow parser is already available in js, it doesn't seem impossible to bring the rest of it to the browser but I'm not aware of anyone working on it




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: