These are very exciting times for us!
Unless you don’t really know that what Fable is exactly? If that happens to be the case, this blog post is meant for you!
We are going to set up a small example from scratch and go through some of the basics. I wish to keep things as simple as possible, so keep in mind that not everything will be “production” ready at the end.
There are a few approaches to get started with Fable. Today we are going to pick the route of least configuration and compile an F# script (*.fsx) using fable-splitter.
After installing the necessary prerequisites, we will need one extra global tool for this project: Paket. Paket will download our .NET dependencies and we use this instead of NuGet to later generate a load script.
Install Paket with
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”install-paket.ps1″]
paket should be available as a cli tool. So we can initialize it in our empty folder.
This creates a new
paket.dependencies file where we shall list all our required .NET dependencies.
Update it to:
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”paket.dependencies”]
paket install. Now that we have our packages, we still need a way to access them. What has happened so far is merely the download of some NuGet packages to our local user cache.
paket generate-load-scripts -t fsx we can generate a file located in the
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”main.group.fsx”]
Once we have the .NET dependencies in place, we can create our
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”App_1.fsx”]
We will install three node modules to our project:
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”yarn.ps1″]
fable-compiler is where the magic happens, that package contains a .NET executable responsible for the compilation.
fable-splitter is means of communicating with the compiler.
You can’t directly compile scripts with
fable-compiler so you need a proxy so to speak.
browser-sync is optional here and it will merely provide us with an easy developers experience.
We’ll create a simple script to start a web server that watches all our files and reloads the browser when we make any changes.
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”server.js”]
The script contains some custom middleware that acts as a workaround for ES6 modules.
Next, we will need a basic
index.html to host our code.
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”index_1.html”]
Lastly, let’s add some npm scripts that will trigger everything.
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”package.json”]
We have two moving pieces in play: the compilation and the web server.
Each can be started by running an npm script in a different terminal.
npm run compile
npm run sync
Now we can open http://localhost:3000 and we should see a log statement inside our developer tools.
Notice that the
App.fsx is compiled to
dist/App.js and uses ES6 modules.
The premise is simple: we have a counter and if a pre-configured time limit has been reached we need to stop the clock and change its background colour.
We’ll add the clock in our
index.html to somewhat match the example in the exercise.
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”index_2.html”]
When we click the button we want to parse the total amount of seconds in our input box and start a timer.
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”App_2.fsx”]
document.querySelector to select any single element that matches a CSS selector. However, if we pass in a selector, that is just a silly string, isn’t it?
One little typo and we actually get
null instead. That is why we can create a little helper function
bySelector that returns an option instead.
[<Emit>()] attribute will replace any function call to
bySelector to output
We have sort of tricked the compiler into telling us that querySelector is unsafe, so it should return a generic option. This is great because the compiler does not let you use the value of an option directly and we can use a more concrete type than Element.
Options are great because we cannot guarantee they even exist. In other words, we are setting up a more elegant way of handling null values.
In the last
match block of code we check if both the button and the input exists before we continue. If they do we wire up a click handler to the button and parse the value.
Once again the helper function
parseInt returns an option of integer instead of a plain integer. Notice that we used
System.Int32.TryParse, in Fable (to some extent) we can leverage our existing .NET knowledge.
Option.iter only runs the function if the option that is passed to it (using the
The beauty of this approach is that nothing can crash at runtime due to null pointer exception. If everything exists, our localStorage cache should be populated with some values that we will use to update the clock.
If it compiles it works!
[gist id=”0aa082d66f8b4d3a39864bbfbee1e4c1″ file=”App_3.fsx”]
On each interval tick, we can use the same defence mechanisms to only proceed when our current state is found (and proven valid) in the localStorage.
Only modify the clock text when the element is found. Each step along the way the compiler will protect us and we can guarantee that the access of each variable is justified.
You can find the source code for this little exercise on GitHub.
matchsyntax, checkout out this page on F# for fun and profit.
Image by Frans
Get to know Axxes and our corporate culture!