F# vs Python development - Giraffe vs Django
By andre
Recently I have been playing around with building a web site using F# through the Giraffe library. This has been an enlightening experience. Most of my previous web development knowledge is from the Python world, specifically Django. In this blog I will try to compare the feel these two so different niches have.
F# vs Python
First things first - F# is a statically compiled functional language, which targets .NET framework. I’m using .NET5 specifically, although, of course, earlier versions have F# support as well. The language has been around for quite some time and is popular in some narrow circles. I find it unfortunate - it is a great programming tool and it enjoys from the rich .NET echo system. A fair comparison would be that F# occupies the same position in the .NET/CLI world vs C# as Scala in JVM vs vanilla Java.
Since F# is statically compiled, by the time you finish fixing all compilation errors and warnings you have squashed 90% of all potential bugs in your code. This is very different from what Python developers have come to expect - even with the modern IDEs, that are helpful in pointing out some of the more obvious errors, we still discover most of the bugs during debugging. If you want to have a stable Python codebase you really MUST write a lot of test cases. Otherwise regressions creep in very easily.
F# on the other hand requires fewer tests thanks to the type checking and its overall tendency towards immutability. By default all values in the language are read-only. One can have mutable variables, but these require explicit declaration and handling. Combined with the functional style of development, which does encourage pure (no-side-effects) behaviour, this prevents whole categories of bugs - and allows us to concentrate our testing on things like the actual functionality and not whether a certain function still has the same signature.
Giraffe vs Django
When we look at the web development framework, the picture is more complex. Django is very feature rich and out of the box provides and amazingly comprehensive array of functionality. It makes developing sites of low to medium complexity very easy. And there is almost always a third party module which you can use to plug any gaps in the desired behaviour. It is worth noting, that Django is quite self contained as well. It of course uses plenty of standard Python modules as well as some third party ones, but most of the web-related functionality can be found inside the framework itself.
Giraffe is not a self contained product. It sits on top of ASP.NET core, which is the main toolbox for web coding in .NET. ASP is a rather old technology, but it has been substantially rebuilt for the new .NET core platform. It is very versatile and configurable, but on its own it doesn’t contain as many features as Django out of the box. There are libraries to make up the shortfall, and we will look at some of them in a bit.
Giraffe is in fact a rather thing layer, making F# development more streamlined, since the core of ASP.NET is all geared towards C# engineers. And in fact there are other libraries, that can be combined with Giraffe, making web development even easier. One example is a set of libraries around the Fable technology, which I will also cover in a moment.
What is important to emphasize, when comparing Django and Giraffe, is that both of them require combining different components in order to have a working product. Since I have had a lot of experience with the Python framework and next to none with F# one, it took me quite a bit to wrap my head around all the configuration and setup required to have even a basic working site. Things would have been much easier if I was familiar with ASP.NET, but I wasn’t. As a result, I had to climb, so to speak, a rather steep learning curve. I think it was worth it, but it is something to bear in mind. If you want to build a very simple web site quickly and you don’t plan to develop it into something much larger, learning all this new stuff might not be worth the trouble.
Data storage
Storing and retrieving database data is a very common activity on any dynamic web site. Django deals with it very well through its models. It is very easy to define objects, representing database records and their relationships. It is shockingly fast to create “migrations”. What I mean by this is that Django will generate the necessary SQL scripts to actually create the tables to store your data in, with all the indices, primary keys, foreign key relationships and so on, as defined by your models. Even more so, it will detect any changes you make to those models and will build incremental updates to alter the database definitions to match your new code. This is very useful and saves a lot of time.
In the ASP.NET world, and by extension in Giraffe, the standard way of dealing with the same problem is by using EntityFramework Core. It can do similar things - map .NET objects to database entities, create migrations and so on. Unfortunately, this library is really C#-centric. Of course, it is possible to use it from F#, which does allow you to invoke any .NET library. However, some of the nicer features of the language are somewhat missing. Mostly this concerns the immutability and support for some F# specific types, such as “option”. As a result, I stopped using EF Core and switched to Dapper and FluentMigrator. Both of these libraries provide less “magic” out of the box. For example, I have to define the migrations myself, using the DB-agnostic API FluentMigrator provides. It is not a huge obstacle, however.
Client interactions
Here we can see something quite interesting. Django is purely a server-side solution. It can generate HTML, which the browser then displays directly. It can also provide a RESTful API of sorts, for a JavaScript-driven client side application to consume. The said client application will be developed separately.
In Giraffe/F# world there is a better way. Of course, server-side HTML is still an option. But if you would like to have an SPA application, written in React, you can do it using F#. It doesn’t mean that some browsers suddenly acquired an ability to run .NET code (Silverlight has been dead for years). Instead, there is a set of libraries called Fable, which compile F# code directly into JavaScript! There are some limitations, of course, but it is very possible and practical to write all or at least most of your client code in this functional and statically checked language. The benefits are multiple and hard to overestimate.
First, as mentioned, your code is statically checked, once again, catching bugs before runtime. Second, in this approach it is possible to share many data structures between server and client code without explicit conversion into JSON or some other format. Of course, the object will be converted, but it will happen automatically and transparently from the developer’s point of view!
The amount of debugging and actual bugs this can save is enormous. Security of the end product is much improved. Testing of various aspects of the code is also greatly simplified, since most of it can be done directly in .NET. Everyone and everything benefits.
Conclusion
Both Django and Giraffe + Fable are viable development platforms. The first allows very fast development of small to medium sites, but for larger code bases there will be a non-trivial penalty to pay due to the dynamic nature of Python. The latter has a steeper learning curve, both for F# and for the underlying ASP.NET core framework, and necessitates dealing with some lack in features. However, it can provide much more robust and secure final product, suffering from fewer bugs and enjoying front to back code unity. It is well suited for larger project, especially if there is an emphasis on correctness and risk aversion.