strict-0.4: src/Data/Strict/Tuple.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE DeriveGeneric #-}
#ifndef __HADDOCK__
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE TypeOperators #-}
#endif
#endif
#if MIN_VERSION_base(4,9,0)
#define LIFTED_FUNCTOR_CLASSES 1
#else
#if MIN_VERSION_transformers(0,5,0)
#define LIFTED_FUNCTOR_CLASSES 1
#else
#if MIN_VERSION_transformers_compat(0,5,0) && !MIN_VERSION_transformers(0,4,0)
#define LIFTED_FUNCTOR_CLASSES 1
#endif
#endif
#endif
-----------------------------------------------------------------------------
-- |
--
-- The strict variant of the standard Haskell pairs and the corresponding
-- variants of the functions from "Data.Tuple".
--
-- Note that unlike regular Haskell pairs, @(x :*: _|_) = (_|_ :*: y) = _|_@
--
-----------------------------------------------------------------------------
module Data.Strict.Tuple (
Pair(..)
#ifndef __HADDOCK__
#ifdef __GLASGOW_HASKELL__
, (:!:)
#endif
#endif
, fst
, snd
, curry
, uncurry
, Data.Strict.Tuple.swap -- disambiguate
, zip
, unzip
) where
-- import parts explicitly, helps with compatibility
import Prelude (Functor (..), Eq (..), Ord (..), Show (..), Read (..), (.), Bounded, map, ($)
, (&&), showParen, showString, readParen, lex, return)
import Control.Applicative ((<$>), (<*>))
import Data.Monoid (Monoid (..))
import Data.Semigroup (Semigroup (..))
import Data.Foldable (Foldable (..))
import Data.Traversable (Traversable (..))
-- Lazy variants
import qualified Prelude as L
import Control.DeepSeq (NFData (..))
import Data.Bifoldable (Bifoldable (..))
import Data.Bifunctor (Bifunctor (..))
import Data.Binary (Binary (..))
import Data.Bitraversable (Bitraversable (..))
import Data.Hashable (Hashable(..))
import Data.Hashable.Lifted (Hashable1 (..), Hashable2 (..))
import Data.Ix (Ix (..))
import GHC.Generics (Generic)
import Data.Data (Data (..), Typeable)
#if __GLASGOW_HASKELL__ >= 706
import GHC.Generics (Generic1)
#endif
#if MIN_VERSION_deepseq(1,4,3)
import Control.DeepSeq (NFData1 (..), NFData2 (..))
#endif
#ifdef MIN_VERSION_assoc
import Data.Bifunctor.Assoc (Assoc (..))
import Data.Bifunctor.Swap (Swap (..))
#endif
#ifdef LIFTED_FUNCTOR_CLASSES
import Data.Functor.Classes
(Eq1 (..), Eq2 (..), Ord1 (..), Ord2 (..), Read1 (..), Read2 (..),
Show1 (..), Show2 (..))
#else
import Data.Functor.Classes (Eq1 (..), Ord1 (..), Read1 (..), Show1 (..))
#endif
#if __HADDOCK__
import Data.Tuple ()
#endif
-- $setup
-- >>> import Prelude (Char, String)
-- >>> import Data.Functor.Classes (readsPrec2)
infix 2 :!:
-- | The type of strict pairs.
data Pair a b = !a :!: !b
deriving (Eq, Ord, Read, Show, Typeable, Data, Generic, Bounded, Ix
#if __GLASGOW_HASKELL__ >= 706
, Generic1
#endif
)
#ifndef __HADDOCK__
#ifdef __GLASGOW_HASKELL__
-- This gives a nicer syntax for the type but only works in GHC for now.
type (:!:) = Pair
#endif
#endif
toStrict :: (a, b) -> Pair a b
toStrict (a, b) = a :!: b
toLazy :: Pair a b -> (a, b)
toLazy (a :!: b) = (a, b)
-- | Extract the first component of a strict pair.
fst :: Pair a b -> a
fst (x :!: _) = x
-- | Extract the second component of a strict pair.
snd :: Pair a b -> b
snd (_ :!: y) = y
-- | Curry a function on strict pairs.
curry :: (Pair a b -> c) -> a -> b -> c
curry f x y = f (x :!: y)
-- | Convert a curried function to a function on strict pairs.
uncurry :: (a -> b -> c) -> Pair a b -> c
uncurry f (x :!: y) = f x y
-- | Analagous to 'L.swap' from "Data.Tuple"
swap :: Pair a b -> Pair b a
swap (a :!: b) = b :!: a
-- | Zip for strict pairs (defined with zipWith).
zip :: [a] -> [b] -> [Pair a b]
zip x y = L.zipWith (:!:) x y
-- | Unzip for stict pairs into a (lazy) pair of lists.
unzip :: [Pair a b] -> ([a], [b])
unzip x = ( map fst x
, map snd x
)
-- Instances
------------
instance Functor (Pair e) where
fmap f = toStrict . fmap f . toLazy
instance Foldable (Pair e) where
foldMap f (_ :!: x) = f x
instance Traversable (Pair e) where
traverse f (e :!: x) = (:!:) e <$> f x
instance (Semigroup a, Semigroup b) => Semigroup (Pair a b) where
(x1 :!: y1) <> (x2 :!: y2) = (x1 <> x2) :!: (y1 <> y2)
instance (Monoid a, Monoid b) => Monoid (Pair a b) where
mempty = mempty :!: mempty
(x1 :!: y1) `mappend` (x2 :!: y2) = (x1 `mappend` x2) :!: (y1 `mappend` y2)
-- deepseq
instance (NFData a, NFData b) => NFData (Pair a b) where
rnf = rnf . toLazy
#if MIN_VERSION_deepseq(1,4,3)
instance (NFData a) => NFData1 (Pair a) where
liftRnf rnfA = liftRnf rnfA . toLazy
instance NFData2 Pair where
liftRnf2 rnfA rnfB = liftRnf2 rnfA rnfB . toLazy
#endif
-- binary
instance (Binary a, Binary b) => Binary (Pair a b) where
put = put . toLazy
get = toStrict <$> get
-- bifunctors
instance Bifunctor Pair where
bimap f g (a :!: b) = f a :!: g b
first f (a :!: b) = f a :!: b
second g (a :!: b) = a :!: g b
instance Bifoldable Pair where
bifold (a :!: b) = a `mappend` b
bifoldMap f g (a :!: b) = f a `mappend` g b
bifoldr f g c (a :!: b) = g b (f a c)
bifoldl f g c (a :!: b) = g (f c a) b
instance Bitraversable Pair where
bitraverse f g (a :!: b) = (:!:) <$> f a <*> g b
-- hashable
instance (Hashable a, Hashable b) => Hashable (Pair a b) where
hashWithSalt salt = hashWithSalt salt . toLazy
instance (Hashable a) => Hashable1 (Pair a) where
liftHashWithSalt hashA salt = liftHashWithSalt hashA salt . toLazy
instance Hashable2 Pair where
liftHashWithSalt2 hashA hashB salt = liftHashWithSalt2 hashA hashB salt . toLazy
-- assoc
#ifdef MIN_VERSION_assoc
instance Assoc Pair where
assoc ((a :!: b) :!: c) = (a :!: (b :!: c))
unassoc (a :!: (b :!: c)) = ((a :!: b) :!: c)
instance Swap Pair where
swap = Data.Strict.Tuple.swap
#endif
-- Data.Functor.Classes
#ifdef LIFTED_FUNCTOR_CLASSES
instance Eq2 Pair where
liftEq2 f g (a :!: b) (a' :!: b') = f a a' && g b b'
instance Eq a => Eq1 (Pair a) where
liftEq = liftEq2 (==)
instance Ord2 Pair where
liftCompare2 f g (a :!: b) (a' :!: b') = f a a' `mappend` g b b'
instance Ord a => Ord1 (Pair a) where
liftCompare = liftCompare2 compare
instance Show a => Show1 (Pair a) where
liftShowsPrec = liftShowsPrec2 showsPrec showList
instance Show2 Pair where
liftShowsPrec2 sa _ sb _ d (a :!: b) = showParen (d > 3)
-- prints extra parens
$ sa 3 a
. showString " :!: "
. sb 3 b
-- |
--
-- >>> readsPrec2 0 "'a' :!: ('b' :!: 'c')" :: [(Pair Char (Pair Char Char), String)]
-- [('a' :!: ('b' :!: 'c'),"")]
--
-- >>> readsPrec2 0 "('a' :!: 'b') :!: 'c'" :: [(Pair (Pair Char Char) Char, String)]
-- [(('a' :!: 'b') :!: 'c',"")]
--
instance Read2 Pair where
liftReadsPrec2 ra _ rb _ d = readParen (d > 3) $ \s -> cons s where
cons s0 = do
(a, s1) <- ra 3 s0
(":!:", s2) <- lex s1
(b, s3) <- rb 3 s2
return (a :!: b, s3)
instance Read a => Read1 (Pair a) where
liftReadsPrec = liftReadsPrec2 readsPrec readList
#else
instance Eq a => Eq1 (Pair a) where eq1 = (==)
instance Ord a => Ord1 (Pair a) where compare1 = compare
instance Show a => Show1 (Pair a) where showsPrec1 = showsPrec
instance Read a => Read1 (Pair a) where readsPrec1 = readsPrec
#endif