Introduction
In this article, we'll be discussing tuples (pronounced “two-pull”) using the F# language.
If you're coming from a different programming language like C# or anything similar it is a good thing because you're already familiar with the .NET tuple type.
Just a note for those coming from the C# language, F# tuples use the underlying System.Tuple<_> type.
OK, let’s get started then
What's A Tuple?
A tuple is an ordered collection of different data types, they can be an integer, float, string, bool, or even a tuple.
Moreover, tuple’s can be denoted by a parenthesis but this is optional and separated by commas.
Create A Tuple
Creating a tuple in F# is quite easy.
You can separate the types via comma and having a parenthesis is optional.
Let's see the examples below.
Creating A Tuple With a Parentheses
[<Fact>]
let ``Create Instance of Tuple With No Parentheses`` () =
(* separate group of values with a comma, and optionally place them within parentheses*)
(* let's start with no parentheses *)
let food = "burger", "chicken"
Assert.True(FSharpType.IsTuple(food.GetType()))
Assert.Equal(typeof<Tuple<string, string>>, food.GetType())
In the given example above we started with no parentheses.
And as we created the tuple, we tested if the type is a tuple by calling the function FharpType.IsTuple().
Then the last assertion is checking if the food is the same as the type of what we expect with the tuple declared.
Creating A Tuple Without a Parentheses
[<Fact>]
let ``Create Instance of Tuple With Parentheses`` () =
(* separate group of values with a comma, and optionally place them within parentheses*)
(* let's have another example with parentheses *)
let cars = ("Toyota", "Mazda")
Assert.True(FSharpType.IsTuple(cars.GetType()))
Assert.Equal(typeof<Tuple<string,string>>, cars.GetType())
In the given example above we have now a tuple with parenthesis.
This time we have used a car as our tuple example and it has the same explanation as the first example.
Creating A Tuple Using Tuple.Create() Method
The examples above are the standard way of creating tuples in F#. However, we can also use the Tuple.Create()
Let's see an example below.
[<Fact>]
let ``Create Instance of Tuple Using Tuple.Create`` () =
let food = Tuple.Create("food", "chicken")
let food2 = Tuple.Create<string,string>("food", "chicken")
let food3 = Tuple.Create<_,_>("food", "chicken")
Assert.True(FSharpType.IsTuple(food.GetType()))
Assert.True(food.GetType() = food2.GetType())
Assert.True(food.GetType() = food3.GetType())
Assert.True(food2.GetType() = food3.GetType())
Usually, this isn't much used but there are still developers who still prefer to do it this way. So it is up to you what you want to use.
Extract Two Elements From a Tuple
Extracting values from a tuple that has two elements is quite easy. We can use the methods fst and snd.
Let's see an example below.
[<Fact>]
let ``Extract Two Elements From Tuple`` () =
let numberOfCPU = ("CPU", 5)
Assert.True(FSharpType.IsTuple(numberOfCPU.GetType()))
Assert.True(typeof<Tuple<string, int>>.Equals(numberOfCPU.GetType()))
Assert.Equal(typeof<Tuple<string, int>>, numberOfCPU.GetType())
Assert.Equal(fst numberOfCPU, "CPU")
Assert.Equal(snd numberOfCPU, 5)
OK, how about when dealing with multiple elements?
Extract Multiple Elements From a Tuple
[<Fact>]
let ``Extract Multiple Identifiers Separated By Commas`` () =
let movies = ("Avengers", "Xmen", "Wolverine", "Gambit")
let movie1, movie2, movie3, movie4 = movies
Assert.Equal(movie1, "Avengers")
Assert.Equal(movie2, "Xmen")
Assert.Equal(movie3, "Wolverine")
Assert.Equal(movie4, "Gambit")
From the example above, if in case you thought that you can try to extract too many or too few values from a tuple, you'll get a compiler error.
Tuple Value Matching (Destructuring)
From our previous examples, we have seen that a tuple builds a value out of two or more other values.
However, retrieving the tuple elements using the fst and snd methods and extracting values from a linear fashion seems not much.
Using matching expressions is beneficial not only for not just for destructuring it but also for matching a value with a pattern.
Let's see an example below.
[<Fact>]
let ``Tuples and Pattern Matching``() =
let CheckIfAdoboExist (food: string*string*int) : bool =
FSharpValue.GetTupleFields(food)
|> Array.contains "Adobo"
let yourFavoriteFood (foodTuple: Option<string * string * int>) :string =
match foodTuple with
| None -> "no favorite food"
| Some v -> match CheckIfAdoboExist(v) with
| true -> "Nice one you like adobo"
| false -> "I can't guess your food"
// _ indicates a wildcard pattern (*) meaning any
let result = yourFavoriteFood(Some("Adobo", "", 1))
Assert.Equal("Nice one you like adobo", result)
let result1 = yourFavoriteFood(Some("", "Adobo", 1))
Assert.Equal("Nice one you like adobo", result1)
let result2 = yourFavoriteFood(Some("", "", 0))
Assert.Equal("I can't guess your food", result2)
let result3 = yourFavoriteFood Unchecked.defaultof < _ > ( * passing null * )
Assert.Equal("no favorite food", result3)
From the example above, it just checks whether your tuple does have an "Adobo" and then if it does it will return a message saying "Nice on you like adobo".
There could be a more complicated example for this one but I just focused on a simple one for us to on how we can set the components apart.
Summary
In this article, we have discussed the following
- What's A Tuple?
- Create A Tuple
- Extract Two Elements From a Tuple
- Tuple Value Matching (Destructuring)
I hope you have enjoyed this article, as I enjoyed it while writing.
You can also find the sample code here on GitHub.
Till next time, happy programming!