Tuples
| A pair is a type | |
| fst and snd | |
| zip and unzip | |
| Tuples or records |
A pair is a type
A tuple groups values whose types may differ. The arity is part of the type, so a pair and a triple are different things and never mix:
person :: (String, Int)
person = ("Ada", 36)
point3 :: (Double, Double, Double)
point3 = (1.0, 2.0, 3.0)
Destructuring happens in a pattern, so a function can take the parts directly instead of receiving the pair:
birthday :: (String, Int) -> (String, Int)
birthday (name, age) = (name, age + 1)
fst and snd
Two standard functions read the halves of a pair. There is no general accessor for larger tuples, because a tuple with many fields is a record waiting to be written:
fst ("Ada", 36) -- "Ada"
snd ("Ada", 36) -- 36
-- swap is a one-liner with a pattern
swap (a, b) = (b, a)
fst and snd are partial in spirit: they only exist for pairs. For triples, write the pattern.
zip and unzip
zip walks two lists in step and stops at the shorter one. unzip takes a list of pairs back apart:
zip [1,2,3] ["a","b"] -- [(1,"a"),(2,"b")]
zipWith (+) [1,2,3] [10,20,30] -- [11,22,33]
unzip [(1,"a"),(2,"b")] -- ([1,2],["a","b"])
The silent truncation of zip is convenient and occasionally a bug. When the lengths must agree, check them or use zipWith with a combining function that reports the mismatch.
Tuples or records
A tuple is right for a value that travels a short distance: a function returning two results, a dictionary entry, an accumulator. It becomes wrong when the meaning of each slot must be remembered:
-- hard to read at the call site
register ("Ada", 36, True)
-- self-documenting, lesson 26
register (Person { name = "Ada", age = 36, active = True })
The rule of thumb: two or three slots used locally are fine, and
anything that crosses a module boundary deserves names.
Next:
Lists
Article author: Arthur Isaev