{-|
Module : Tuples
Description : Work with several values without creating a new data structure.
License : BSD 3
Maintainer : terezasokol@gmail.com
Stability : experimental
Portability : POSIX
There is built-in syntax for tuples, so you can define 2D points like this:
> origin :: (Float, Float)
> origin =
> (0, 0)
>
> position :: (Float, Float)
> position =
> (3, 4)
This module is a bunch of helpers for working with 2-tuples.
Note 1: For more complex data, it is best to switch to records. So instead
of representing a 3D point as `(3,4,5)` and not having any helper functions,
represent it as `{ x = 3, y = 4, z = 5 }` and use all the built-in record
syntax!
Note 2: If your record contains a bunch of `Bool` and `Maybe` values,
you may want to upgrade to union types. Check out [Joël’s post](https://robots.thoughtbot.com/modeling-with-union-types) for more
info on this.
-}
module Tuple
( -- * Create
pair
-- * Access
, first, second
-- * Map
, mapFirst, mapSecond, mapBoth
)
where
-- CREATE
{-| Create a 2-tuple.
> -- pair 3 4 == (3, 4)
>
> zip :: List a -> List b -> List (a, b)
> zip xs ys =
> List.map2 Tuple.pair xs ys
-}
pair :: a -> b -> (a, b)
pair a b =
(a, b)
-- ACCESS
{-| Extract the first value from a tuple.
> first (3, 4) == 3
> first ("john", "doe") == "john"
-}
first :: (a, b) -> a
first (x, _) =
x
{-| Extract the second value from a tuple.
> second (3, 4) == 4
> second ("john", "doe") == "doe"
-}
second :: (a, b) -> b
second (_, y) =
y
-- MAP
{-| Transform the first value in a tuple.
> import String
>
> mapFirst String.reverse ("stressed", 16) == ("desserts", 16)
> mapFirst String.length ("stressed", 16) == (8, 16)
-}
mapFirst :: (a -> x) -> (a, b) -> (x, b)
mapFirst func (x, y) =
(func x, y)
{-| Transform the second value in a tuple.
> mapSecond sqrt ("stressed", 16) == ("stressed", 4)
> mapSecond negate ("stressed", 16) == ("stressed", -16)
-}
mapSecond :: (b -> y) -> (a, b) -> (a, y)
mapSecond func (x, y) =
(x, func y)
{-| Transform both parts of a tuple.
> import String
>
> mapBoth String.reverse sqrt ("stressed", 16) == ("desserts", 4)
> mapBoth String.length negate ("stressed", 16) == (8, -16)
-}
mapBoth :: (a -> x) -> (b -> y) -> (a, b) -> (x, y)
mapBoth funcA funcB (x, y) =
(funcA x, funcB y)