Using immutable data structures in F#

Microsoft F# Fsharp

An immutable data structure (or object) is a structure whose value doesn’t change after it’s created. When you declare a data structure that contains some values, you store these values in slots, such as a field or value declaration. In functional programming, all these slots are immutable, which leads to the use of immutable data structures. In this section, we’ll demonstrate the simplest built-in immutable data type. You’ll see more common functional data structures in the upcoming chapters.

Introducing tuple type

The simplest immutable data structure in F# is a tuple type (the u in the word tuple is pronounced as the same as in cup). Tuple is a simple type that groups together several values of (possibly) different types. The following example shows how to create a value, which contains two values grouped together:

> let tp = ("Hello world!", 42);;
val tp : string * int

Creating a tuple value is fairly easy: we write a comma-separated list of values enclosed in parentheses. But let’s look at the code in more detail. On the first line, we create a tuple and assign it to a tp value. The type inference mechanism of the F# language is used here, so you don’t have to explicitly state what the type of the value is. The F# compiler infers that the first element in the tuple is of type string, and the second is an integer, so the type of the constructed tuple should be something like a tuple containing a string as the first value and an integer as the second value. Of course, we don’t want to lose any information about the type, and if we represented the result using only some type called, for example, Tuple, we wouldn’t know that it contains a string and an integer.

The inferred type of the expression is printed on the second line. You can see that in F# a type of a tuple is written as string * int. In general, a tuple type is written as types of its members separated by an asterisk. In the next few sections, you’ll see how tuples can be used in F#, but we’ll also show you how to use the same functionality in C#. If you don’t immediately understand everything after reading the F# code, don’t worry; continue with the C# examples, which should make everything clearer.

In C#, we can use a type that’s already available in .NET 4.0, but we’ll also look how to implement it to better understand the internal workings of tuples. Representing tuples in C# is possible thanks to generics. We can use them to implement a type that can store different values in a type-safe fashion. Using generics, the C# equivalent of the F# type string * int will then be Tuple<string, int>. We’ll get to the C# version shortly after discussing one more F# example.

Working with Tuples in F#

Let’s look at more complicated F# code that uses tuples. In the following code, we use tuples to store information about a city. The first member is a string (the name of the city) and the second is an integer, containing a number of people living there. We implement a function printCity, which outputs a message with the city name and its population, and finally we create and print information about two cities.

Working with tuples (F# Interactive)
let printCity(cityInfo) =
    printfn "Population of %s is %d." (fst cityInfo) (snd cityInfo)

let prague = ("Prague", 1188126)
let seattle = ("Seattle", 594210)

printCity(prague)
printCity(seattle)

This code shows a session from F# Interactive, so you can easily try it for yourself. The first piece of code ❶ declares a function printCity, which takes information about the city as an argument and prints its value using the standard F# printfn function. The formatting string specifies that the first argument is a string and the second is an integer. To read the first and second element of the tuple, we use two standard F# functions, fst and snd, respectively (which obviously represent first and second).

The next line shows the type of the function deduced by the F# type inference. As you can see, the function takes a tuple as an argument (denoted using an asterisk: string * int) and doesn’t return any value (denoted as the unit type on the right side of the arrow symbol). This is exactly what we wanted.

Next, we create two tuple values ❷ that store population information about Prague and Seattle. After these lines are entered, the F# Interactive shell prints the types of the newly declared values, and we can see that the values are of the same tuple type that the printCity function takes as an argument. That means we can pass both values as an argument to our printing function and get the expected result ❸.

Working with Tuples in C#

As we mentioned, if we use .NET 4.0, we can work with an existing generic type, Tuple<T1, T2>, from the System namespace. We’ll look how to implement a type like that shortly.

In any case, the type we’ll work with will have a single constructor with two parameters of types T1 and T2 respectively. It’ll also have two properties for accessing the values of its members, so unlike in F# where we accessed the elements using the functions fst and snd, in C# we’ll use the properties Item1 and Item2. The following code has the same functionality as the F# code, but it’s written in C#.

Working with tuples (C#)

The translation from F# code to C# is straightforward once we have an equivalent for the F# tuple type in C#. The PrintCity method takes a tuple of string and int as an argument. In C# we have to specify the types of method arguments explicitly, so you can see that the type of the cityInfo parameter is Tuple<string, int> ❶. The method prints the information using the .NET Console.WriteLine method and uses properties of the tuple type (Item1 and Item2) to read its value ❷. Next, we initialize two tuples that store information about the cities using a constructor with two arguments ❸, and we use the PrintCity method to show the information ❹.

Next post is about using functions as values.

One thought on “Using immutable data structures in F#

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.