Fable F# — Why and How

I’ve recently become very interested in Fable which allows you to produce javascript output from F# input. Fable is built on quite an array of other, well-established technologies — a good thing, don’t reinvent the wheel — but as a result, it can be quite daunting to get what’s going on into your head.

It’s also evolved quite rapidly and continues to do so, which means that quite a lot of what’s out there about it is out of date to some degree, and it is, in my view unfortunate that the project name is a normal English word, making it hard to construct online searches which avoid multiple false positives.

But, having got through that learning curve, in my opinion Fable offers more than enough benefit to justify the investment in time and effort required. This article is intended to help reduce that time and effort — and indeed to help remind me of things I may forget.

Everything below relates to what at the time of writing is the current version of Fable v1.3.11 and the current version of F# v4.3

Technologies used

The main reason why Fable can be confusing to start is that it relies on two, normally separate, technology stacks.

On the one hand, its tools and runtime environment live firmly in the javascript world of node.js and browser deployment tools like webpack and yarn.

On the other hand, the infrastructure to support the F# language itself, the compilation environment, and the build process, rely on Microsoft’s .NET, which itself has multiple sub-variants, including the traditional .NET framework bundled with all Windows systems, the mono workalike for Linux/Unix, and the .NET core framework, which is Microsoft’s opensource implementation of much of the .NET framework.

And the magic which brings both technology stacks together relies heavily on babel, which is usually thought of as a way of allowing code written in more recent versions of javascript to be deployed to older browsers, but is actually much more than that.

Getting Started — prerequisites

Our first step is to make sure that we can actually create a basic Fable application. I’m going to recommend you follow the official docs to ensure you have the various prerequisites, but once you’ve installed all the tooling you need, come back here before you run any other commands.

The link you need is: http://fable.io/docs/getting-started.html ( first section only ).

Our first Fable app

OK, so we’ve installed .net sdk, node, yarn &c and some kind of editor ( I recommend VS code at this point, but it’s up to you ).

The next stage is to create a minimal Fable app, and take a look at the result. So:

dotnet new -i Fable.Template

will install the basic Fable Template in your environment. Now you can say

dotnet new fable -n FableApp -lang F#
cd FableApp

and if all goes well, you will have a brand new directory containing all sorts of stuff.

Before we continue, let’s look around. At the time of writing ( 2018–03–15 ), FableApp contains:

  • A README.md file which gives more detail than I do about how to build and run the application, but less detail about the structure of some of the key files.
  • Files called .gitattributes and .gitignore and a directory called .vscode which I won’t say more about as they are not directly relevant.
  • A FableApp.sln file which allows the dotnet restore command to know where to find the various projects for which to restore missing packages.
  • A file Nuget.Config which is there to make sure that Microsoft’s nuget tooling doesn’t interfere with the use of paket to manage .net side dependencies
  • A public directory containing an icon. This directory is where one would put the files served by our web browser — more later on this.
  • top level files paket.dependencies and paket.lock. paket.dependencies is where we tell paket which NuGet packages we need in our project. paket.lock tells paket the precise versions of those packages we have used, and allows us to ensure that future builds from source are repeatable even if downstream packages change to new versions. (See the paket docs for the full explanation )
  • a yarn.lock file which serves the same purpose for javascript packages as paket.lock does for nuget ones.
  • a tools directory from which the fable compiler, webpack &c are configured and run
  • and last, but definitely not least, a package.json file.

Now, before we look at some of the details, lets just run the app. The latest template makes that very easy. Just say:

yarn install

which will download all needed javascript dependencies, and then, thanks to a line in package.json, does the same for the dotnet side

The result will be a node_modules directory containing lots of javascript packages.

The .net equivalent of yarn install is

dotnet restore

but you won’t need to do this separately as the latest template arranges for yarn install to call it for you thanks to a line in package.json.

BTW, note that dotnet restore calls paket under the hood thanks to the configuration in the .sln and .fsproj files.

At this point, our directory has been supplemented with everything we need to build and run. So first try:

yarn build

This will call the build script defined in package.json, which changes to the tools directory and runs the dotnet fable command with webpack. This does several things:

  • It looks in the fsproj and paket.references files in the current directory (tools) to find out how to interpret dotnet fable as a call to a package called dotnet-fable
  • dotnet-fable then creates a daemon which listens on a socket for instructions to run the fable transpiler
  • It then runs webpack with command parameters which cause it to read the webpack.config.prod.js file for instructions.
  • That in turn uses the fable-loader package for webpack which can read the project file, identify the f# sources to compile, and send them to the fable daemon
  • Webpack then takes the resulting javascript and packages it up all ready to run.

The result is placed into a build directory and consists of an icon from the public directory, a .js file containing all the necessary javascript to run the application, and an index.html file based on the one in the src directory but with a reference to the javascript bundle.

At this point we could simply send index.html to our favourite browser, and see the result.

But the template also provides us with

yarn start

which allows us to run in development mode. This uses the webpack-dev-server to build the application in development mode and then publish it on a small webserver at port 8080. It also monitors the source for changes, and automatically rebuids the application when necessary.

webpack-dev-server has lots of clever features, and is well-documented in its own right. But the following may be useful if you’re not already familiar with it

In addition to simply serving the static files and bundle needed for a browser-client, as in the current template, webpack and webpack-dev-server can

  • Produce a bundle suitable for running as a node application rather than as a browser client.
  • Simultaneously bundle for both client and server, thus allowing an application written entirely in F# to be deployed with node and a browser entirely in javascript.
  • Have the webpack-dev-server act as a passthrough-proxy to a separate webserver. This allows us to coexist our browser-app with a server-backend written for example in F# for .net core using Giraffe or indeed any backend we like, not just one written using node.

At this point, I encourage you to do the following

  • Look at the code in the src directory, make some small changes, and check that you can run the result.
  • Look at the package.json and webpack…js files and refer to the documentation for yarn and webpack to make sure you understand them and what they do. ( This is the only way to get comfortable with these key tools in my opinion, and without that knowledge, sooner or later you will get unstuck as your projects increase in complexity )

Once you are happy that you can extend the project, you have several places to go next

  • You can look at the SAFE-Bookstore sample which will show you a structure for a Fable application with an F# .NET backend and an F# Fable client.
  • You can look at the fableconf-workshops repo, and in particular the Elmish app, which will show you the structure for a Fable application with a node backend written in F# and a web frontend, also written in F#
  • You can learn more about Fable Javascript interop, which will give you techniques for leveraging pretty much any existing Javascript library from F#.
Like what you read? Give Gary Bilkus a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.