strict-0.4: src/Data/Strict/Either.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE DeriveGeneric #-}
#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 'L.Either' type and the
-- corresponding variants of the functions from "Data.Either".
--
-- Note that the strict 'Either' type is not an applicative functor, and
-- therefore also no monad. The reasons are the same as the ones for the
-- strict @Maybe@ type, which are explained in "Data.Maybe.Strict".
--
-----------------------------------------------------------------------------
module Data.Strict.Either (
Either(..)
, either
, isLeft, isRight
, fromLeft, fromRight
, lefts, rights
, partitionEithers
) where
-- import parts explicitly, helps with compatibility
import Prelude ( Functor (..), Eq (..), Ord (..), Show (..), Read (..), Bool (..), (.), ($)
, error, Ordering (..), showParen, showString, lex, return, readParen)
import Control.Applicative (pure, (<$>))
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 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
-- | The strict choice type.
data Either a b = Left !a | Right !b
deriving (Eq, Ord, Read, Show, Typeable, Data, Generic
#if __GLASGOW_HASKELL__ >= 706
, Generic1
#endif
)
toStrict :: L.Either a b -> Either a b
toStrict (L.Left x) = Left x
toStrict (L.Right y) = Right y
toLazy :: Either a b -> L.Either a b
toLazy (Left x) = L.Left x
toLazy (Right y) = L.Right y
-- | Case analysis: if the value is @'Left' a@, apply the first function to @a@;
-- if it is @'Right' b@, apply the second function to @b@.
either :: (a -> c) -> (b -> c) -> Either a b -> c
either f _ (Left x) = f x
either _ g (Right y) = g y
-- | Yields 'True' iff the argument is of the form @Left _@.
--
isLeft :: Either a b -> Bool
isLeft (Left _) = True
isLeft _ = False
-- | Yields 'True' iff the argument is of the form @Right _@.
--
isRight :: Either a b -> Bool
isRight (Right _) = True
isRight _ = False
-- | Extracts the element out of a 'Left' and throws an error if the argument
-- is a 'Right'.
fromLeft :: Either a b -> a
fromLeft (Left x) = x
fromLeft _ = error "Data.Strict.Either.fromLeft: Right"
-- | Extracts the element out of a 'Right' and throws an error if the argument
-- is a 'Left'.
fromRight :: Either a b -> b
fromRight (Right x) = x
fromRight _ = error "Data.Strict.Either.fromRight: Left"
-- | Analogous to 'L.lefts' in "Data.Either".
lefts :: [Either a b] -> [a]
lefts x = [a | Left a <- x]
-- | Analogous to 'L.rights' in "Data.Either".
rights :: [Either a b] -> [b]
rights x = [a | Right a <- x]
-- | Analogous to 'L.partitionEithers' in "Data.Either".
partitionEithers :: [Either a b] -> ([a],[b])
partitionEithers =
L.foldr (either left right) ([],[])
where
left a ~(l, r) = (a:l, r)
right a ~(l, r) = (l, a:r)
-- Instances
------------
instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
instance Foldable (Either e) where
foldr _ y (Left _) = y
foldr f y (Right x) = f x y
foldl _ y (Left _) = y
foldl f y (Right x) = f y x
instance Traversable (Either e) where
traverse _ (Left x) = pure (Left x)
traverse f (Right x) = Right <$> f x
instance Semigroup (Either a b) where
Left _ <> b = b
a <> _ = a
-- deepseq
instance (NFData a, NFData b) => NFData (Either a b) where
rnf = rnf . toLazy
#if MIN_VERSION_deepseq(1,4,3)
instance (NFData a) => NFData1 (Either a) where
liftRnf rnfA = liftRnf rnfA . toLazy
instance NFData2 Either where
liftRnf2 rnfA rnfB = liftRnf2 rnfA rnfB . toLazy
#endif
-- binary
instance (Binary a, Binary b) => Binary (Either a b) where
put = put . toLazy
get = toStrict <$> get
-- bifunctors
instance Bifunctor Either where
bimap f _ (Left a) = Left (f a)
bimap _ g (Right a) = Right (g a)
first f = either (Left . f) Right
second g = either Left (Right . g)
instance Bifoldable Either where
bifold (Left a) = a
bifold (Right b) = b
bifoldMap = either
bifoldr f _ c (Left a) = f a c
bifoldr _ g c (Right b) = g b c
bifoldl f _ c (Left a) = f c a
bifoldl _ g c (Right b) = g c b
instance Bitraversable Either where
bitraverse f _ (Left a) = fmap Left (f a)
bitraverse _ g (Right b) = fmap Right (g b)
-- hashable
instance (Hashable a, Hashable b) => Hashable (Either a b) where
hashWithSalt salt = hashWithSalt salt . toLazy
instance (Hashable a) => Hashable1 (Either a) where
liftHashWithSalt hashA salt = liftHashWithSalt hashA salt . toLazy
instance Hashable2 Either where
liftHashWithSalt2 hashA hashB salt = liftHashWithSalt2 hashA hashB salt . toLazy
-- assoc
#ifdef MIN_VERSION_assoc
instance Assoc Either where
assoc (Left (Left a)) = Left a
assoc (Left (Right b)) = Right (Left b)
assoc (Right c) = Right (Right c)
unassoc (Left a) = Left (Left a)
unassoc (Right (Left b)) = Left (Right b)
unassoc (Right (Right c)) = Right c
instance Swap Either where
swap (Left x) = Right x
swap (Right x) = Left x
#endif
-- Data.Functor.Classes
#ifdef LIFTED_FUNCTOR_CLASSES
instance Eq2 Either where
liftEq2 f _ (Left a) (Left a') = f a a'
liftEq2 _ g (Right b) (Right b') = g b b'
liftEq2 _ _ _ _ = False
instance Eq a => Eq1 (Either a) where
liftEq = liftEq2 (==)
instance Ord2 Either where
liftCompare2 f _ (Left a) (Left a') = f a a'
liftCompare2 _ _ (Left _) _ = LT
liftCompare2 _ _ _ (Left _) = GT
liftCompare2 _ g (Right b) (Right b') = g b b'
instance Ord a => Ord1 (Either a) where
liftCompare = liftCompare2 compare
instance Show a => Show1 (Either a) where
liftShowsPrec = liftShowsPrec2 showsPrec showList
instance Show2 Either where
liftShowsPrec2 sa _ _sb _ d (Left a) = showParen (d > 10)
$ showString "Left "
. sa 11 a
liftShowsPrec2 _sa _ sb _ d (Right b) = showParen (d > 10)
$ showString "Right "
. sb 11 b
instance Read2 Either where
liftReadsPrec2 ra _ rb _ d = readParen (d > 10) $ \s -> cons s
where
cons s0 = do
(ident, s1) <- lex s0
case ident of
"Left" -> do
(a, s2) <- ra 11 s1
return (Left a, s2)
"Right" -> do
(b, s2) <- rb 11 s1
return (Right b, s2)
_ -> []
instance Read a => Read1 (Either a) where
liftReadsPrec = liftReadsPrec2 readsPrec readList
#else
instance Eq a => Eq1 (Either a) where eq1 = (==)
instance Ord a => Ord1 (Either a) where compare1 = compare
instance Show a => Show1 (Either a) where showsPrec1 = showsPrec
instance Read a => Read1 (Either a) where readsPrec1 = readsPrec
#endif