In the last days I completed the first version of FFRAB-Mobile (see here). I used F# and Xamarin.Forms to gain more experience in a bigger project than a simple example app. You can find the sources here: https://github.com/SabotageAndi/ffrab-mobile
Here are my impressions:
Coming from a C# background, now writing code that is near complete stateless is new and unusual. So the first code I wrote was a lot of object oriented stuff with too much access to shared state. Especially the access to the database connection was at the beginning a little bit painful.
But after some refactoring it got more and more in a functional style. At least I hope so.
Before refactoring:
https://github.com/SabotageAndi/ffrab-mobile/blob/0e72e26e83f5112e6c95cc99959baa9a55eb9882/ffrab.common/model.fs Line 52 – 140
After refactoring:
https://github.com/SabotageAndi/ffrab-mobile/blob/master/ffrab.common/database.fs https://github.com/SabotageAndi/ffrab-mobile/blob/master/ffrab.common/queries.fs
At the end it was worth the trouble. It is more readable and simpler code.
When you have pure F# code and libraries, it is complete in your hands to write nice code. But when you are using C# libraries, i can quickly get ugly because of the library. This is because the parameters of the C# methods are represented as tuples. With them, you have a lot of brackets and can not use the forward pipe operator ‘|>’.
But simply wrap the C# method call in a small function and voila.
Here is an example when using NodaTime (the “let startTime”- line is the important one): Direct call to C#:
let dateTimeFormat = OffsetDateTimePattern.CreateWithInvariantCulture("yyyy'-'MM'-'dd'T'HH':'mm':'sso<G>")
let startTime = common.Formatting.dateTimeFormat.Parse(dayNode.["day_start"].Value<string>()).Value
With F# wrapper function:
let parseNodaTime<'T> (pattern : NodaTime.Text.IPattern<'T>) rawValue =
let result = pattern.Parse(rawValue)
result.Value
let dateTimeFormat = OffsetDateTimePattern.CreateWithInvariantCulture("yyyy'-'MM'-'dd'T'HH':'mm':'sso<G>")
let startTime = (json.GetProperty "day_start").AsString() |> parseNodaTime common.Formatting.dateTimeFormat
For one usage it might be not that bad, but when you have multiple calls, it is more readable. Particularly you get the nice left- to- right readability back!
Look at this example:
let synchronizeData conference =
match conference with
| Some conference ->
conference
|> checkForTimeout
|> getDataLocation
|> fetchJson
|> Parser.parseJson conference
|> Synchronization.sync conference
| _ ->
ignore()
conference
All parts of the synchronization are nice one after one step easy readable and extendable. So adding the additional timeout check was easy to add to this pipeline.
With FSharp.ViewModule and F# you have so much less boilerplate code in your viewmodels. Without the curly brackets of C# you can reduce the line count again.
So the 8 viewmodels are about 260 lines (with empty lines and boilder plate functions/type). In C# 3 of the viewmodels would have this code size.
When you code in Visual Studio, you need the Visual F# Power Tools. And the first thing you have to do is to enable Folder organization. You do not need the folders for your F# code, but to organize other files like images or json-Files. No idea why it is disabled by default.
On the Xamarin side there is sadly one bug, that prevents to use Xamarin.Forms on Android with F#. It looks like the F# CodeDom does not escape F# keywords. For details is here the Bugzilla entry: https://bugzilla.xamarin.com/show_bug.cgi?id=24709 Hopefully the bug is fixed soon.
For the C# developers with Resharper: First you think you will miss it, but you do not need it. The Power Tools have a Rename function. Other refactoring features like extracting or adding namespaces I didn’t miss.
So for me is F# a really fun and productive language. Not everything is easy and clear when you are using it the first time, but the fog will lift with the time. When you are working some days concentrated with F#, you will learn and understand a lot. And it will be awesome. 😉