What I liked and disliked about F#

Auteur
Casper Broeren
Categorieƫn
Publiceer datum

This year I moved and while unpacking I found this book in my collection which I kind of forgot; 'Real World Functional Programming with examples in F# and C#' by Tomas Petricek and Jon Skeet. I had the book for more than 3 years and I never finished it. When the moving was done, I started reading the book again, because my internet connection was busted. But the book was great. It guides you through all the concepts of F# step by step and explains the C# counterpart as well. The book is a must read.

After I finished the book I developed a taste for that sweet F# code and bought 'F# for Quantitative Finance' by Johan Astborg. It was even more thrilling than the first book. Most of all because we at VI company we create solutions for the finance industry of which the book covers diverse topics in this field.

But what is so great about F#? Well, I can give you my five reasons

  1. Immutability ; F# is a (semi)functional language opposed the declarative language C#. This holds certain ground rules like every variable should be immutable. It may seem like a tiny change but has great impact. This makes code more predictable and better when using multiple threads. Of course, F# is a bit flexible because you can declare variables immutable but that is not the way to do it. The way to do it is to just think differently about a certain problem. For example; I wanted to adjust a window of data, so it would contain the same amount of windows as the source collection, first I created it like this;
let windowedFitted1( items:seq<_>, n) : seq<_[]> = 
    let mutable result = Seq.windowed n items
    let framesToAdd = n-1
    for i=1 to framesToAdd do
          let frame:seq<_> = Seq.take i items
          result <-  Seq.append result [Seq.toArray frame]
    result 

After developing this, I refactored this to the code below. As you can see I didn't had to use mutable and went from 4 to 2 lines of code

let windowedFitted( items:seq<_>, n) : seq<_[]> =
Seq.windowed n items
    |> Seq.append  (seq {for i=1 to n-1  do  yield Seq.toArray(Seq.take i items) } )
  1. Pattern matching; Pattern matching in F# can be compared with a switch statement in C#, only it goes much further. Beside evaluation of type and directly casting it, pattern matching can also do evaluations in a case (known as 'guards'). View the example below; because of type inferring, only the strings are allowed in the function valueTest. Second the guard will take the testValue and test it on certain chars in string. Only then we print out the correct message.
let valueTest testValue =
    match testValue with
    | var1 when String.exists(fun x-> x = 'c' || x = 'a' || x = 's') var1  -> printfn "Has a c, a  or s in it"
    | _ -> printfn "No correct chars"

valueTest "Casper" // results in Has a c, a  or s in it
valueTest "Pete" // results in No correct chars

This makes matching on types and value more powerful and handy to use. Consider every time you have an if in F#, writing as a pattern and include the fall through case as well which can be expressed with a lower dash. However C# is now catching up. In C# 7 there is something that looks like the F# counterpart.

  1. Pipe operator; Since immutability is king in the land of F# it's common to use piping operators. A lot of operations just give you the new value, you can chain your operations. The coolest thing is that you can adjust the direction as needed for your code. The two last lines in this sample render the same result. This isn't an earth-shattering example, but I think it shows how flexible F# is in writing
let foldfunction = (fun acc x -> acc + x)
let items = seq[0..10]
items  |> Seq.fold foldfunction 0 // sums up all items to 55
Seq.fold foldfunction 0 <| items // sums up all items to 55
  1. Collections; Most of the time in F# you're dealing with heaps data. Collections of native types like string, int or decimal. Sometimes tuples or record types or plain classes. In general, there are three flavors of collections; arrays, lists and sequences. The first two are well known from C# and need no further explanation. The sequence or seq can be compared to enumerable. A sequence features a head and tail. The head is the current item at the front, the tail is the rest of the collection or just the end. Having control over the tail and head gives you great power. For instance, you can do 'tail recursion'. This is just like regular recursion but then you start from the end. This way you don't have to either store data on the stack like normal recursion and hop back all items on the stack when the tail has been reached (or you're faced with an actual stack-overflow exception). Tail recursion is faster and lighter than normal recursion as is shown in the example below (for an extensive article on the topic check out http://blog.ploeh.dk/2015/12/22/tail-recurse/ )
let countTailRec items =
    let rec loop items acc =
        match items with
            | [] -> acc
            | h::t -> loop t (acc + h)
    loop (items |> List.rev) 0
let items = seq[0..10] |> Seq.toList
countTailRec items
  1. Community; F# is developed by F# Software foundation and Microsoft. Microsoft threats F# as a "first-class citizen of the dotnet ecosphere". This means F# is backed by an independent foundation but also adopted in dotNet. But what makes F# stand out is that there is an active community. There are sites like https://fsharp.org/learn.html and https://Fsharpforfunandprofit.com/ which explain in depth the workings of F# and make it understandable for people like me. Second there are great libraries like F#.Data. This grants you access to your data structure at design time, even when it's remote. I almost lost my mind how cool this is. While using the url in the HtmlProvider, the url is automatically queried and properties are created on the type. This means .Tables and later on x.Version are generated at design time. Go checkout https://github.com/fsharp/FSharp.Data
open FSharp.Data
[<Literal>]
let url = "https://en.wikipedia.org/wiki/F_Sharp_programming_language"
type wikipedia = HtmlProvider<url>
let FSharpWiki = wikipedia.GetSample()
printfn "%s"  FSharpWiki.Tables.Versions.Name
FSharpWiki.Tables.Versions.Rows
|> Seq.toArray
|> Array.map (fun x -> printfn "Version: %A" x.Version)
|> ignore

These 5 items are just a small part of what makes F# and its community great. It's not weird to say that

But besides all the positive experiences and great sources, F# has some challenges šŸ˜‰

  1. Type inference; This is a double-edged sword since most of the time you just are in type inference bliss. F# deducts your type, and you don't have to be so declarative about the types you want to use. But then you're going deeper and types need to be cast, and certain functions require specific types... it's just a pain in the ass. It always feels like settling for less when explicitly defining variables.
  2. Units of measure; First when I discovered this functionality I was very pleased. How cool to just express a decimal as certain measure! For example;
    [<Measure>]
    type EUR
    let allowance=10.0<EUR>

    My problem lies in the fact that when F# code is compiled the Intermediate Language doesn't know about the measure and so we cannot do more cool things with it at runtime. That is a real bummer. It's nice that at design and compile time we get checks and validation on the measure, but I think F# is really throwing away information by not make it possible to deduce the measure at runtime.

  3. Ignore
    As we saw at my F# data snippet in point 5, I needed to tell F# that I won't be doing anything with my collection coming from rows. I must implicitly tell to ignore it. I do like to be verbose but could ignore just be a little bit more elegant? Maybe use |# to indicate we no longer need the result?!

To end on a happy note: F# is a very beautiful language and is something each C# developer should look at. Quite some features we use today in C# are coming from F# or functional paradigms. In closing I don't expect we at VI company will be switching out C# for F#, but still it's nice to do something different occasionally. Thanks for Sebastiaan for reviewing and coming up with a better title.

Terug naar boven

Deel anonieme data?

We vinden privacy belangrijk en volgen de nieuwe GDPR regels en voorschriften. Dit betekent dat we een opt-in voor het gebruik van Google Analytics aanbieden.