I’ve written previously about JavaScript-free web development using F#, but there are other options if you don’t have the luxury of starting a new project with F#. One of those is Fable, which has a pretty low barrier to entry in terms of integration with existing projects.
Fable compiles F# to JavaScript. So it’s not the most elegant solution, but perhaps, it’s a justifiable alternative to writing JavaScript directly.
Most Fable guides assume that you’re starting a new project and that you’re using webpack. But neither of these are required, and it’s actually pretty straightforward to integrate Fable into an existing app if all you have available is npm
or yarn
. Using webpack does result in a slightly cleaner integration, but if you’re not already using it, adding webpack to your toolchain is like driving a screw with a sledgehammer.
Alternatively, you can get by with just the fable-compiler
and fable-splitter
packages.
By using fable-splitter
, you will end up with JavaScript modules that map one-to-one with your F# modules. They can then be imported into other JavaScript sources.
1. Prerequisites
Download and install the .NET Core SDK (even if it’s already installed, it can be useful to make sure you’re using the latest version). The Fable compiler uses this when compiling F# sources. It’s also helpful for managing and testing the F# project.
Add the fable-splitter
and fable-compiler
packages to your JavaScript project (e.g. yarn add fable-compiler fable-splitter
).
2. Create an F# Project
There are some Fable project templates for dotnet, but I didn’t find them to be useful. Again, they’re mostly intended for starting a new app. A standard F# library project will do just fine for integrating with an existing app.
First, create a directory within your JavaScript project to hold F# project files. Switch into that directory, and then run dotnet new library --language F#
.
Optionally, you can add the Fable.Core package with dotnet add package Fable.Core
. This package contains some useful modules for JavaScript interop.
3. Compile F# to JavaScript
At this point, compiling the F# sources to JavaScript should be as simple as invoking fable-splitter. From the F# project directory, running fable-splitter *.fsproj -o output
should give you a pile of JavaScript files in the output/
directory.
4. Integration
How you integrate this into your project is up to you. In my case, I integrated it into an Ember.js project with just a yarn script entry to invoke fable-splitter. This means that F# sources must be compiled manually when changed, and the resulting JavaScript must be checked into source control. This approach has the advantage of not requiring any changes to the build pipeline. The downside is a manual step when modifying F# sources.
Gotchas
Name mangling
F# supports multiple modules within a single source file, whereas JavaScript does not. So Fable will mangle module names in order to ensure uniqueness. This is only a problem if you need to call F# functions from JavaScript, so just make sure you have one module per file in that case.
Numeric types minefield
I ran into a problem early on when trying to use 64-bit unsigned integers. JavaScript numbers are always 64-bit floats, so the illusion of 64-bit integer support is added by way of an object that manipulates a pair of numbers representing the high and low 32 bits. It’s not perfect though, and I found the JavaScript version of the F# pown
function to be unreliable. Watch out for this, and test edge cases carefully.
Testing
So, should tests be written in JavaScript or F#? It’s certainly possible to go either way, but I would argue for writing unit tests of F# code in F#. Unit tests ought to be as close as possible to the system under test. And while Fable appears to be pretty stable, it does add another layer of complexity. The points of integration between “native” JavaScript and Fable-compiled JavaScript are the only places that I would say tests should be written in JavaScript.