Skip to content

Tuples

We have seen code examples that used pairs before. A pair of values of types a and b has the type (a, b). We can access the elements of a pair using pattern matching. We also have helper functions

fst :: (a, b) -> a
fst (x, _) = x

snd :: (a, b) -> b
snd (_, y) = y

So, fst p gives us access to the first component of the pair p, and snd p gives us access to the second component of the pair p. Their implementations demonstrate how to pattern match against pairs.

Since we can only pattern match against variables, wildcards, and data constructors, this suggests that (,) is a data constructor for pairs. And, indeed it is:

GHCi
>>> (,) 1 2
(1,2)

Of course, we're not restricted to working with only pairs:

GHCi
>>> (1,1+1,1+1+1)
(1,2,3)

Nor do the elements of a pair have to have the same type:

GHCi
>>> :t ("Hello",True,1)
("Hello",True,1) :: Num c => (String, Bool, c)

As expected, the first component of this triple has type String, and the second one has type Bool. The type of the third component cannot be determined without more context. Since we use the integer literal 1 for this component, all that GHCi can deduce is that it must be a number type: Num c => c.

We can also define quadruples, quintuples, and so on, but of course, large tuple types are often better replaced with custom record types whose fields have names that document what they represent.

Another wrinkle when working with tuples beyond pairs is that fst and snd work only for pairs. That should be obvious: The type signature fst :: (a, b) -> a says that the argument of fst must be a pair, not a triple or quadruple, and the same for snd.

The standard library does not provide any helpers analogous to fst and snd for tuples beyond pairs. Of course, we could define them ourselves:

fst3 :: (a, b, c) -> a
fst3 (x, _, _) = x

snd3 :: (a, b, c) -> b
snd3 (_, y, _) = y

thd3 :: (a, b, c) -> c
thd3 (_, _, z) = z

but it is often more elegant to simply use pattern matching directly in the functions that need to gain access to the components of a tuple or, even better, to define appropriate custom data types for representing groupings of more than two values.