Fable entered its fourth year with a new major version that greatly improves its performance, code-generation, and stability. It took more than six months to go from the initial design discussions to a first beta of Fable 2, and after a few more months of beta testing, Fable 2.1 was finally released.

Among the initial design goals for Fable 2, one of most relevant was implementing F# records as plain plain JS objects and unions as array. This has been finally scrapped due to the impossibility of preserving F# full semantics but in the process Fable's type system became lighter mostly due to the fact reflection code is now generated only when actually used. Other notable changes for the programmer concern the POJO (Plain Old JavaScript Object) attribute, which is not required in Fable 2 for JavaScript interoperability, and JSON serialization being not included with Fable.Core anymore due to the availability of community-provided serializers such as Fable.SimpleJson.

Fable 2 also includes many internal changes to make it play better with JavaScript tooling like bundlers and minifiers. In particular, Fable 2 will prefer converting both class methods and nested module, As a matter of fact, the effort to keep a one-to-one correspondence between F# and JavaScript language constructs wherever possible created a number of JavaScript anti-patterns that at times defeated JavaScript tools.

InfoQ has spoken with Alfonso García-Caro, Fable creator and maintainer.

InfoQ: What improvements can developers expect from Fable 2?

Alfonso García-Caro: There've been many improvements in the Fable 2 release, specially around stability, bundle size and performance of the compiled applications. Basically all of the bugs reported were fixed and the tool is now very reliable. We've also worked a lot in reducing the size of the production bundles generated with Fable without having to sacrifice the benefits of the F# language and its core library, and we managed to do that by playing well with the JS module system and tools like Webpack. In general, it's expected that Fable 2 applications are around 40% smaller after minification. About performance, there's still room to improve. But depending on your application, we've seen cases of JS code running 2x faster compared to Fable 1. One of our test benches is the Fable REPL which fully runs on the client side by turning the F# compiler and Fable itself into JS, which is a great entry point to F# and Fable and thanks to the performance improvements it has become a very pleasant development environment (Firefox doesn't seem to like it very much, though).

InfoQ: Fable 2 is an almost complete rewrite of Fable 1. What was the reason for starting from scratch?

García-Caro: Because a rewrite is always the answer! Tell that to your managers... Just kidding. Writing compilers was something new for me and I learned a lot about that and about the F# and JS languages when developing Fable 1. So this new major release was a great opportunity to do a cleanup, discard the ideas that didn't work, focus on those that did and try a few new ones. Another very important principle was to make the code more accessible to other contributors than myself. This is not ideal yet, but Fable 2 development has been a much more collective task and I'm very happy of the result.

InfoQ: Can you share a few lessons your learned from the rewrite?

García-Caro: When writing a compiler you're always thinking in performance, both of the generated code and the compiler itself. And it's very tempting to put performance above everything else. But nobody needs something that it's very fast if it doesn't produce the desired results. So Fable 2 puts the focus in correctness and only when everything works fine we apply the optimization. And the truth is, this has actually better performance in most situations. Another lesson I learned is how to make the ideas behind a program more explicit through the use of types, without relying on implementation details. That's the whole point of using a language like F#, modeling your domain, but it's easy to forget. In Fable 2, the language constructs that had special treatment in Fable, like options, lists or uncurried functions, have been promoted to get their own entries in the Fable AST. That way, if we need to change how options are implemented, for example, we only need to touch the last step without breaking anything else.

InfoQ: Fable 2 has a new architecture as well, which makes place for an explicit optimization phase in between the AST generation and JavaScript generation phases. What are the advantages that this brought?

García-Caro: Well, the architecture is still more or less the same: we take the AST from the F# compiler, which parses and checks the code, then we transform it to Fable's own AST so we can manipulate it while keeping the type information, and transform it again to a JSON AST we send to Babel to generate the actual JS code. But it's true that in Fable 1 we didn't have an intermediate optimization phase, so all the optimizations were like patches applied to either the F#-to-Fable or Fable-to-Babel step. Now we do have this optimization phase, which also consists of several isolated steps on its own. This, together with the improved AST, is a much better way to try to apply optimizations (like beta reduction or in the case of Fable, function uncurrying), because whenever you detect a problem you can just disable the optimization and quickly confirm if it was causing the issue or not. And although it looks like this will make the compiler much slower because you need to traverse the AST more often, it's actually not the case (mainly because we're applying the beta reduction first which greatly reduces the size of the AST).

InfoQ: The Fable ecosystem has also grown significantly in the last year thanks to many contributions from the community. Could you highlight two or three of the most significant of them and comment on their importance for Fable developers?

García-Caro: Only two or three? There are many great projects although we're still a small community. Actually, more than the compiler itself, my current focus is in helping some of these projects and update the website to provide better documentation and a tool to make the libraries more visible. Most Fable users already know about Elmish which has become the standard architecture for Fable apps. I also use the tools by Maxime Mangel like Fulma or Thoth libraries everyday. Another prolific contributor is Zaid-Ajaj who, among many useful packages, has published Fable.Remoting: a tool to automatically generate a REST API between your client and your server. For a different approach, Diego Esmerio created Elmish.Bridge to automatically propagate the Elmish state between the server and the client through websockets. Finally we have Fable.Reaction to combine the power of ReactiveX with Elmish. And we shouldn't forget about SAFE to write web apps with full-stack F#. Please visit the site of each project for more information or check the videos from the latest FableConf for more in-depth talks.

InfoQ: Finally, you just moved back to NPM for Fable distribution, after living temporarily in NuGet. What motivated this change and what benefits does it bring?

García-Caro: Fable has always had two hearts: .NET to communicate with the F# compiler and JS to communicate with Babel (although we already have an experimental version fully working in JS). This means two processes but the key is who calls whom. Originally we had a Node process calling the dotnet one, but in the move to .NET Core we gave the new dotnet CLI tools a try. The evolution of this tools however has been a bit confusing and there are still some limitations. Additionally, since the most popular way to use Fable is together with Webpack through the fable-loader, it made sense to go back to NPM so Fable could easily be invoked from JS. It's still necessary to install the dotnet SDK on your computer, though. If you want to read the whole story, check this tweet out.

You can get Fable 2 from NPM. It requires dotnet SDK 2.0 or higher and yarn. If you want to port existing Fable 1 code to Fable 2, do not miss this transition guide.