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:
>>> (,) 1 2
(1,2)
Of course, we're not restricted to working with only pairs:
>>> (1,1+1,1+1+1)
(1,2,3)
Nor do the elements of a pair have to have the same type:
>>> :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.