As you may know from previous blog post in this series, we love F#. Unfortunately, web browers only support JavaScript. After showing you all these great F# tools we use on the back end, are we just going to write JS on the front end? Fable provides a wondelful alternative! It's a F# to JavaScript compiler, enabling us to use our beloved language on the front end!
Before we go into how we can use Fable to make JS development more enjoyable, let's make a quick comparison between F# and JS
Typing
F# is a strongly typed language, which means that function arguments are expected to be a specific type. JavaScript is weakly typed. A classic example of a problem that weakly typed languages are sensitive to is the behavior of the +
operator:
let x = (1 + 1) / 2 // 1
let y = (1 + "1") / 2 // 5.5
The first line speaks for itself, and makes perfect sense. The second line is funky! Because one of the arguments to the +
operator is a string, JavaScript interprets it as string concatenation, resulting in "11"
as an intermediate result. Because the /
operator can only take numbers, it converts it back to the number 11 and devides that by 2! Clearly not the intended behaviour.
The F# alternative is satisfyingly boring:
let x = (1 + 1) / 2 // 1
let y = (1 + "1") / 2 // Compiler Error: The type 'string' does not match the type 'int'
Using the + operator with two different types is simply not possible! We can solve the issue by casting the string to an int; because we are forced to do this before the +
operator is used, the addititon makes sense again:
let value = (1 + (int)"1") / 2 // 1
Compilation
Javascript is an interpreted language. You don't have to compile it first: the browser can execute the code directly from the .js files. This makes developing in JavaScript very accessible, since you don't need to install a compiler. On the other hand, it also means there is no way to know whether the code you are executing is technically correct: whether a function exists or not will only become apparent when you try to execute it. During the compilation process that turns F# into executable code, the compiler will run into all kinds of problems if you write incorrect code, eliminating a whole class of errors that can occur in interpreted code.
A clear paradigm
Another disadvantage of JavaScript is that it tries really hard to please everyone. Because JavaScript is the only way to add interactivity to web pages, JavaScript facilitates various programming paradigms. This leads to a huge standard, creating a lot of ways to do the same thing. This can make that codebases can look very different based on the techniques that the author uses.
F# on the other hand, is deliberately designed to be a productive, functional programming language. It has a relatively limited, but very focussed toolkit that allows you to write functional code, without the need for a very deep understanding of concepts like monads, like you would for writing Haskell.
Fable: Compile F# into javascript
By compiling F# to JavaScript, Fable eliminates the downsides of writing JavaScript yourself. This means we can write strongly typed code, with a clear functional paradigm, and have the safety of a compiled language to avoid runtime errors, all while being able to run our code in the browser!
Setting up Fable is easy enough, but if you're using the SAFE template, it's all set up for you. We're using the same language on the front end and the back end, which presents two huge advantages: Code reuse on the backend and front end, and the reduced cognitive load of switching between language. There's no need to switch between a whole different model with it's own types, and we can even use Fable Remoting to transfer this data between the front- and backend!
A big advantage of the widespread use of JavaScript is the huge collection of free libraries available. Good news: Fable has great support for JavaScript Interop. While writiting interopt code is not trivial, it's definitely doable, and if you need a place to get started, read this blog post. There are also a lot of wrappers available as NuGet packages; one of our favourites is Feliz, an F# wrapper around React. More about that in a later post in this series!
Readable JavaScript output
While the output of a traditional compiler is generally not human-parsable, Fable's output is actually pretty readable, maintaining the symbols used in the original F# code. Here's a small sample in F#:
let isPretty name =
match name with
| "Joost" -> true
| _ -> false
let data = [
"Benno"
"Theodore"
"Frank"
"Joost"
]
data
|> List.map (fun x -> (x, isPretty x))
|> List.iter (fun x -> printfn "%A" x)
After using the Fable online REPL to compile the code, we are left with this:
import { map, iterate, ofArray } from "fable-library-js/List.js";
import { printf, toConsole } from "fable-library-js/String.js";
export function isPretty(name) {
if (name === "Joost") {
return true;
}
else {
return false;
}
}
export const data = ofArray(["Benno", "Theodore", "Frank", "Joost"]);
iterate((x_1) => {
const tupledArg = x_1;
toConsole(printf("%A"))([tupledArg[0], tupledArg[1]]);
}, map((x) => [x, isPretty(x)], data));
Pattern matching does not exist in JavaScript, so we get a simple If/Else statement, but it's still wrapped in a function with the same name. In the same way, our data is converted to the appropriate JavaScript data type, but maintains the original name. The output of the pipelined operations is a tiny bit harder to parse. Because they are converted to simple function calls, the order in which we see the operations changes.
Discriminated unions require a bit more boilerplate than our previous example, but it still returns a reasonable small and readable output, as you can see in this example.
While the Fable compiler is generally pretty stable, it's very nice to be able to have a peek at what it spits out, especially when debugging interop use.
Debugging using source mapping
Attached to debugging using break points? Fable has got your back with source mapping. Using source linking you can set breakpoints in your F# code, that causes the javascript output to break. Check out the SAFE docs to see how to use source mapping.
Conclusion
Fable provides a way to compile F# to Javascript. You get all the benefits of F# with very little overhead, while still having access to the huge JavaScript array of JavaScript packages available.