Tuples
Multiplying types together
Last updated
Was this helpful?
Multiplying types together
Last updated
Was this helpful?
We're ready for our first extended type -- the tuple.
Let's start by stepping back again and looking at a type such as "int". As we hinted at before, rather than thinking of "int" as a abstract thing, you can think of it as concrete collection of all its possible values, namely the set {...,-3, -2, -1, 0, 2, 3, ...}.
So next, imagine two copies of this "int" collection. We can "multiply" them together by taking the Cartesian product of them; that is, making a new list of objects by picking every possible combination of the two "int" lists, as shown below:
As we have already seen, these pairs are called tuples in F#. And now you can see why they have the type signature that they do. In this example, the "int times int" type is called "int * int
", and the star symbol means "multiply" of course! The valid instances of this new type are all the pairs: (-2,2),(-1,0), (2,2) and so on.
Let's see how they might be used in practice:
Now if you evaluate the code above you will see that the types of t1 and t2 are int*int
as expected.
This "product" approach can be used to make tuples out of any mixture of types. Here is one for "int times bool".
And here is the usage in F#. The tuple type above has the signature "int*bool
".
Strings can be used as well, of course. The universe of all possible strings is very large, but conceptually it is the same thing. The tuple type below has the signature "string*int
".
Test the usage and signatures:
And there is no reason to stop at multiplying just two types together. Why not three? Or four? For example, here is the type int * bool * string
.
Test the usage and signatures:
Generics can be used in tuples too.
The usage is normally associated with functions:
And the function signature is:
which means that "genericTupleFn
" takes a generic tuple ('a * 'b)
and returns a unit
Any kind of type can be used in a tuple: other tuples, classes, function types, etc. Here are some examples:
Some key things to know about tuples are:
A particular instance of a tuple type is a single object, similar to a two-element array in C#, say. When using them with functions they count as a single parameter.
Tuple types cannot be given explicit names. The "name" of the tuple type is determined by the combination of types that are multiplied together.
The order of the multiplication is important. So int*string
is not the same tuple type as string*int
.
The comma is the critical symbol that defines tuples, not the parentheses. You can define tuples without the parentheses, although it can sometimes be confusing. In F#, if you see a comma, it is probably part of a tuple.
These points are very important -- if you don't understand them you will get confused quite quickly!
The tuple types in F# are somewhat more primitive than the other extended types. As you have seen, you don't need to explicitly define them, and they have no name.
It is easy to make a tuple -- just use a comma!
And as we have seen, to "deconstruct" a tuple, use the same syntax:
When pattern matching like this, you must have the same number of elements, otherwise you will get an error:
If you don't need some of the values, you can use the "don't care" symbol (the underscore) as a placeholder.
As you might guess, a two element tuple is commonly called a "pair" and a three element tuple is called a "triple" and so on. In the special case of pairs, there are functions fst
and snd
which extract the first and second element.
They only work on pairs. Trying to use fst
on a triple will give an error.
Tuples have a number of advantages over other more complex types. They can be used on the fly because they are always available without being defined, and thus are perfect for small, temporary, lightweight structures.
It is a common scenario that you want to return two values from a function rather than just one. For example, in the TryParse
style functions, you want to return (a) whether the value was parsed and (b) if parsed, what the parsed value was.
Here is an implementation of TryParse
for integers (assuming it did not already exist, of course):
Here's another simple example that returns a pair of numbers:
As with most F# values, tuples are immutable and the elements within them cannot be assigned to. So how do you change a tuple? The short answer is that you can't -- you must always create a new one.
Say that you need to write a function that, given a tuple, adds one to each element. Here's an obvious implementation:
This seems a bit long winded -- is there a more compact way? Yes, because you can deconstruct a tuple directly in the parameters of a function, so that the function becomes a one liner:
Tuples have an automatically defined equality operation: two tuples are equal if they have the same length and the values in each slot are equal.
Trying to compare tuples of different lengths is a type error:
And the types in each slot must be the same as well:
Tuples also have an automatically defined hash value based on the values in the tuple, so that tuples can be used as dictionary keys without problems.
And it is worth re-iterating the point made in : don't mistake tuples for multiple parameters in a function.
And as noted in a , tuples have a nice default string representation, and can be serialized easily.