F# by example: discriminated unions, record types, tuples and operators.
Contents
Last week I continued learning F# with my friends Samir and Pedro. The first week we learned some of the F# basics and this week we learned some of the functional characteristics of F#. To do that we started doing a custom interpretation of the Bank kata from Sandro Mancuso. You can find the code here: https://github.com/vgaltes/FSharpByExample/tree/master/BankKata
As usual, let's start with a test an its implementation:
https://gist.github.com/vgaltes/a9c109919fe7e5555c27
In this piece of code we can see some of the functional characteristics of F# we are going to see in this post. The first one is Discriminated Unions. Discriminated unions are types whose value are restricted to a known set of values called union cases. These cases are the only valid values. In our case we are defining Currency as a discriminated union. One of the most used discriminated unions is the Option type, present in the F# core library:
https://gist.github.com/vgaltes/ad0eaab4d86ce2677b86
Option is a type with two possible values: Some and None. None is an empty union case (doesn't contain any associated data). Some has an associated instance of 'T. It's something similar to Maybe pattern in C#.
Using Option type is quite simple:
https://gist.github.com/vgaltes/d1dda90150ff380166ac
We will an example of how to retrieve the value of an Option, but it's as simple as:
https://gist.github.com/vgaltes/5da2202b0dfd5e833a2c
The second functional characteristic of F# we can see in our little piece of code is a record type. In our case, we used a record type to define what is an account. A record represent a simple aggregates of named values. It's a kind of type between a tuple and a class. To define a record type enclose between braces a list of labels with type annotations. A simple record type can be something like this:
https://gist.github.com/vgaltes/ee0e0e0260b4cecd53b1
So, in our example we are defining an Account as a record type that has a label called Amount that is a tuple of int and Currency.
Let's take a look at the deposit function's code after another test and a refactor:
https://gist.github.com/vgaltes/7297d59862b4007c980e
Here we can see how type inference and pattern matching (tuple pattern) helps us to have a more compact code. First of all, instead of defining the last parameter of the function as an Account, we can use definition of the record type. Doing this, we can use tuple pattern to extract the values of the tuple and use them inside the function. In our case, we don't care about the second value of the tuple, so we can use the wildcard pattern (_).
Let's take a look now at the final implementation of the withdraw function:
https://gist.github.com/vgaltes/eea502a4bc3b2ce9475a
In this piece of code we can see another flavor of pattern matching. In contrast with the one used in the previous article, here we can't use directly the values calculated in the match part to define our cases. We have to use the values to compare with another value, in this case 0. To do that, we use the keyword when, specifying the condition after it. In the second part (when the first case is false) we get the calculated value and use it in the return.
And finally, let's take a look at the codification of a global operator we use to chain withdrawals:
https://gist.github.com/vgaltes/c9b2b5cd166c2babcf31
As you can see, you define an operator just like another function but with the name of the operator enclosed in parenthesis. In our case, the operator |>> takes two parameters called result and f. You can annotate the type if you want but there's no need to. In the body of the function we use pattern matching to determine if the first parameter is a Success or a Failure. If it's a Success we called the function f with value as its first parameter and if it's a Failure we return the failure.
And that's all folks! We are just learning F# so, if you find something that is not correct in the article, please, leave a comment!
Author Vicenç García
LastMod 04-05-2015