My experience of real-world F# project – building a web-based application using F#, Suave, PostgreSQL, mono, vue.js and Visual Studio Code, developed and run on Linux.
A little bit of background
For a project that I have undertaken, I decided to give F# a try. I have been interested in F# for a number of years. Microsoft has been working on making .NET available and more appealing to developers working on and running their programs on other platforms than Windows. Examples of these activities are the .NET Core, ASP.NET Core and Visual Studio Code. Other people and companies investing into F# for their products. As a result, I decided that the time has come to give F# finally a try.
I have used two functional languages before – Erlang and Elixir. Elixir has been attracting developers in the last couple of years mostly due three aspects. It has a friendlier syntax than Erlang. It has the ability to use Erlang’s mature ecosystem. And there is the super Phoenix web framework that is clearly designed to make web development as easy and rapid as possible.
For me, there are these significant aspects:
- F# can use .NET ecosystem and I am familiar with it.
- The functional way of programming resonates with me.
- The F#-specific ecosystem has been growing and more people than just the language enthusiast are using it now.
- F# is statically typed. This brings benefits.
- F# provides inspiration for C#.
F# is a language that is not new. It’s been developed by Microsoft here in the UK since mid 2000’s, and new features continue to be added. The syntax is nothing like C#. Similarly to Coffeescript, white spaces in F# matter. They separate arguments, and indentations act like curly braces in C# or any other language with C syntax to separate blocks of code and scopes. I experienced white space significance for the first time when writing code in Coffeescript back in 2012 and I grew to like it. The quite unusual thing about F# is that it is a functional language but it can be used in the imperative world of classes and OO – .NET. This gives a lot of extra flexibility.
F# allows you to use both, an imperative and a functional style of programming. An imperative way of programming is used in most main-stream languages like C# or Java. To give programs written the imperative way some structure, OO was invented and a multitude of patterns have been developed.
Functional programming is for languages that treat functions as the most basic building blocks. You define what input they take and what output they give. Everything they that need to do their work is passed into them. You can pass functions around and into other functions like you pass values and references to objects in OO languages.
Functional languages tend to not use variables but to bind values to “labels”. Typically you can’t mutate a value in a functional language. F# actually allows you to do so, when you declare your intent explicitly. You’re basically saying “I know this is normally a bad idea but I really need to do it here”.
As you will imagine, the functional way of programming requires different ways of controlling the direction that your program takes. There is many less loops and if’s and much more other patterns through which you express the logic of the program. This is likely an area where most of your time will be spent when learning a functional language.
Asynchronous, parallel and concurrent
F# has a rich offering of methods that allow you to make elements of your program run concurrently, asynchronously and in parallel. This is very useful when writing pieces of software that need to scale, not block a thread or run in non-sequential fashion. Until fairly recently, this has been a challenge, usually leading to complex and otherwise hard to comprehend code.
This is an area where C# has been taking inspiration from F#m it seems. The support of iterators, async methods and generic tasks are the results of these efforts.
In both C# and F#, there is now a way to separate, in a better way, the logic of your program from the mechanics required to run the code in non-sequential fashion. F# makes this even easier with its Asynchronous flows (an implementation of the Promise mechanism that allows code to be written in a sequential manner but that executes asynchronously under the bonnet), Agents, etc. I find this another appealing part of F#.
The project parts
The project included a web-server with a REST API, a database for persistence, and a rich client in the browser.
As the database, I chose PostgreSQL for its reliability, independence, and ability to scale and provide an HA solution, as well as its rich functionality to support document based format (JSON) when you don’t need the relational stuff.
For the client side, I decided to use Vue.js again. It’s quite a delightful, straightforward framework that takes me back to the Angular days due to its declarative way of writing templates, but requiring a shallower learning curve to achieve good results. It’s light-weight, it performs very well in comparison with Angular and React, and it enjoys a rich community and ecosystem of functionality (router, state management vuex, to name a couple) and tools, including a smashing browser plugin that gives access to everything you need to know about the Vue.js instances running in the page.
There is a hot-reload functionality – courtesy of webpack – that allows you to make changes to your templates, css, and code, and being able to instantly see the results without your webpage being reloaded and losing all the state in many cases. This can save a ton of time.
The web server
For the web server, I chose Suave. It’s an HTTP server designed and built for the functional world of F#. Besides Windows, it will run happily on Linux or OS X, either on mono or DotNet Core. It is non-blocking. When a request comes in, it caries everything in it, and a function that you provide to deal with the request will return a promise to the server.
Routing is simple, the server is not in your way, it just does its server’y stuff, nothing more nothing less, sweet. There is a way to set your development environment up so that when you change your server code, the changes will be picked up and the web server restarted automatically. Again, very useful and convenient.
In production, I usually stick this kind of server applications behind a reverse proxy (nginx) where I do SSL/TLS offloading, so I don’t need to worry about it in the actual server code. Suave can deal with HTTPS if need be, though. There is also some support for WebSockets which I am quite fond of because of the ability to push data to the client and of the performance benefits over an HTTP-based API.
For the editor to actually write my code in, I chose Visual Studio Code. I installed it on my Mac as well as on my Linux workstation. I left Windows as my primary OS at the beginning of 2016 and moved to Linux. Initially, it has not been the easiest of moves. Mostly because of my laptop’s Skylake CPU architecture that was not that well supported at that time in the Linux kernel, but things are pretty good now and I don’t really miss Windows.
I have used a few editors and IDEs in Linux but I am not hard-core enough to use vi. I tried Atom but it proved slow and unreliable when I was using it. Sublime was OK but it did not grab my heart.
Visual Studio Code is definitely a very nice tool. It is responsive and it seems to enjoy a lot of enthusiastic support. My only gripe with it is with Intellisense that still needs some work to make it work as smoothly as in the regular Visual Studio. This could well be a problem with Ionide, the plugin that supports F# development in VS Code.
In VS Code, you get Git information about all project files bu default – very useful.
In general, VS Code feels good to someone like me who purchased personal licences for Visual Studio 2005, 2008 and has been using the line of the product for years at work and at home.
Data persistence, ORM or not
In F#, you’re probably best developing your code in a persistence-agnostic manner. This may be hard, as one typically starts with the database or entities, but in F# you’re really steered towards using F# types to model your domain logic. Persistence is a kind of secondary matter.
To access a database, there are a number of choices. Again, I decided to try something else than Entity Framework, LINQ2SQL or Dapper, so I chose an open source SQL provider with a rather generic name SQLProvider. This provider is happy to speak to multiple database engines from SQL Server to PostgreSQL, Oracle and MySQL.
F# SQL Provider
F#’s SQLProvider is a so-called erasing type provider for F#. Traditionally, you create/generate code for entities describing the objects in your database – your database tables, views or procedures and functions – and you do it either manually or with the help of some sort of program that reverse engineers your database objects.
With SQLProvider, you don’t do this. Based on a connection string that you define in your code, you find that VS Code is suddenly speaking to the database. It’s a strange feeling. The types (entities in ORM speak) describing your database objects are actually generated on the fly in the IDE, and they are not stored in the code nor in the resulting assembly. Probably, hence the name ‘erasing type provider’. Intellisense in VS Code picks these up as you’re writing your queries or data accessors as if they were code entities that you previously prepared or generated. If you add a column to a table or change a view, the changes are reflected in the IDE pretty much straight away.
The experience was not 100% smooth and I will try to find out where the problem is, but it was usable enough.
Other than that, the SQLProvider has quite rich functionality offering LINQ queries on many supported keywords, relationships, asynchronous operations, and CRUD operations to name a few. Before you decide to rely on it, check if it supports your use cases.
One thing that I missed are database migrations. They allow you to describe your database tables and objects or the changes that you wish to apply to them. Migration scripts are then stored in your code repository. I believe they first appeared in the .NET world as part of the Entity Framework.
However, the truth is that this functionality does not need to come with your database driver or an ORM necessarily. You can use a separate component – and there is one, node.js based, it’s called db-migrate. Since node.js is used for the front-end stuff – it is needed by webpack, it’s no problem to install it and use it.
Building and running in production
For the F# part of the system, I used the Paket manager to manage .NET dependencies, and FAKE to build the project.
The template provided with vue-loader comes with webpack and a full configuration to build the front-end for production. This was the simplest experience when building a front-end project ever.
Running on a server
I deployed the application on an Ubuntu server, put it behind an nginx reverse proxy. I encountered two problems at this point: an old mono runtime. The one that came from the standard Ubuntu repository was very slow. A new version was fine. The other problem was related to IP binding. Using the loopback interface – localhost – behind the reverse proxy was no good. Instead of binding to the actual IP of the virtual server where the program runs, I configured the Suave server to bind to 0.0.0.0.
Even in a small-scale project like this, there was plenty of new things to learn. It prolonged the project by a few days, but it was worth it. The experience was an enjoyable one and I am definitely going to use F# again.