either-n-0.1.0.0: src/Data/Either3/Either3.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wall #-}
-- | A data type similar to @Data.Either@ with three constructors.
module Data.Either3.Either3 (
-- * Data type
Either3 (..),
-- * Catamorphism
foldEither3,
-- * Isomorphisms
either3ACB,
either3BAC,
either3BCA,
either3CAB,
either3CBA,
-- * Optics
-- ** Classy lenses
GetEither3 (..),
HasEither3 (..),
-- ** Classy prisms
ReviewEither3 (..),
AsEither3 (..),
) where
import Control.DeepSeq (NFData)
import Control.Lens (Each (..), FoldableWithIndex (..), FunctorWithIndex (..), Getter, Iso, Lens', Prism', Review, TraversableWithIndex (..), iso, lens, prism', review, unto, view)
import Control.Monad.Zip (MonadZip (..))
import Control.Selective (Selective (..), selectM)
import Data.Bifoldable (Bifoldable (..))
import Data.Bifunctor (Bifunctor (..))
import Data.Bifunctor.Swap (Swap (..))
import Data.Bitraversable (Bitraversable (..))
import Data.Data (Data)
import Data.Functor.Alt (Alt (..))
import Data.Functor.Apply (Apply (..))
import Data.Functor.Bind (Bind (..))
import Data.Functor.Classes (Eq1 (..), Eq2 (..), Ord1 (..), Ord2 (..), Show1 (..), Show2 (..), showsUnaryWith)
import Data.Functor.Extend (Extend (..))
import Data.Lens.Injection.Injection1 (Injection1)
import Data.Lens.Injection.Injection2 (Injection2)
import Data.Lens.Injection.Injection3 (Injection3)
import GHC.Generics (Generic, Generic1)
{- $setup
>>> import Control.Lens((^?), (#), over, view, review, toListOf, imap)
>>> import Control.DeepSeq(rnf)
>>> import Data.Data(toConstr)
>>> import GHC.Generics(from, to)
>>> import Control.Monad.Zip(mzip)
>>> import Control.Selective(select)
>>> import Data.Bifunctor(bimap)
>>> import Data.Bifoldable(bifoldMap)
>>> import Data.Bitraversable(bitraverse)
>>> import Data.Bifunctor.Swap(swap)
>>> import Data.Functor.Alt((<!>))
>>> import Data.Functor.Apply((<.>))
>>> import Data.Functor.Bind((>>-))
>>> import Data.Functor.Classes(liftEq, liftCompare)
>>> import Data.Functor.Extend(duplicated)
>>> import Data.Lens.Injection.Injection1(Injection1(..))
>>> import Data.Lens.Injection.Injection2(Injection2(..))
>>> import Data.Lens.Injection.Injection3(Injection3(..))
-}
{- | A value of one of three types, similar to 'Either'.
The 'Functor', 'Applicative' and 'Monad' instances act on the third type
parameter, so 'First3' and 'Second3' short-circuit, in the same way 'Left'
does for 'Either'.
>>> toConstr (Second3 True :: Either3 String Bool Int)
Second3
>>> to (from (Third3 1 :: Either3 String Bool Int)) :: Either3 String Bool Int
Third3 1
-}
data Either3 a b c
= First3 a
| Second3 b
| Third3 c
deriving stock (Data, Eq, Generic, Generic1, Ord, Show)
deriving anyclass (NFData)
{- | Case analysis for 'Either3'.
>>> foldEither3 length fromEnum (+ 1) (First3 "abc" :: Either3 String Bool Int)
3
>>> foldEither3 length (const 0) (+ 1) (Third3 1 :: Either3 String Bool Int)
2
-}
foldEither3 :: (a -> x) -> (b -> x) -> (c -> x) -> Either3 a b c -> x
foldEither3 f _ _ (First3 a) =
f a
foldEither3 _ g _ (Second3 b) =
g b
foldEither3 _ _ h (Third3 c) =
h c
{-# INLINE foldEither3 #-}
{- | Reorder the type parameters of 'Either3' to @a c b@.
>>> view either3ACB (Second3 True :: Either3 String Bool Int)
Third3 True
>>> view either3ACB (Third3 1 :: Either3 String Bool Int)
Second3 1
>>> over either3ACB (bimap show not) (Second3 True :: Either3 String Bool Int)
Second3 False
-}
either3ACB :: Iso (Either3 a b c) (Either3 a' b' c') (Either3 a c b) (Either3 a' c' b')
either3ACB =
iso (foldEither3 First3 Third3 Second3) (foldEither3 First3 Third3 Second3)
{-# INLINE either3ACB #-}
{- | Reorder the type parameters of 'Either3' to @b a c@.
>>> view either3BAC (First3 "x" :: Either3 String Bool Int)
Second3 "x"
>>> view either3BAC (Second3 True :: Either3 String Bool Int)
First3 True
-}
either3BAC :: Iso (Either3 a b c) (Either3 a' b' c') (Either3 b a c) (Either3 b' a' c')
either3BAC =
iso (foldEither3 Second3 First3 Third3) (foldEither3 Second3 First3 Third3)
{-# INLINE either3BAC #-}
{- | Reorder the type parameters of 'Either3' to @b c a@.
>>> view either3BCA (First3 "x" :: Either3 String Bool Int)
Third3 "x"
>>> view either3BCA (Second3 True :: Either3 String Bool Int)
First3 True
>>> review either3BCA (First3 True :: Either3 Bool Int String)
Second3 True
-}
either3BCA :: Iso (Either3 a b c) (Either3 a' b' c') (Either3 b c a) (Either3 b' c' a')
either3BCA =
iso (foldEither3 Third3 First3 Second3) (foldEither3 Second3 Third3 First3)
{-# INLINE either3BCA #-}
{- | Reorder the type parameters of 'Either3' to @c a b@.
>>> view either3CAB (First3 "x" :: Either3 String Bool Int)
Second3 "x"
>>> view either3CAB (Third3 1 :: Either3 String Bool Int)
First3 1
>>> review either3CAB (First3 1 :: Either3 Int String Bool)
Third3 1
-}
either3CAB :: Iso (Either3 a b c) (Either3 a' b' c') (Either3 c a b) (Either3 c' a' b')
either3CAB =
iso (foldEither3 Second3 Third3 First3) (foldEither3 Third3 First3 Second3)
{-# INLINE either3CAB #-}
{- | Reorder the type parameters of 'Either3' to @c b a@.
>>> view either3CBA (First3 "x" :: Either3 String Bool Int)
Third3 "x"
>>> view either3CBA (Second3 True :: Either3 String Bool Int)
Second3 True
-}
either3CBA :: Iso (Either3 a b c) (Either3 a' b' c') (Either3 c b a) (Either3 c' b' a')
either3CBA =
iso (foldEither3 Third3 Second3 First3) (foldEither3 Third3 Second3 First3)
{-# INLINE either3CBA #-}
{- |
>>> fmap (+ 1) (Third3 1 :: Either3 String Bool Int)
Third3 2
>>> fmap (+ 1) (Second3 True :: Either3 String Bool Int)
Second3 True
-}
instance Functor (Either3 a b) where
fmap =
mapEither3
{-# INLINE fmap #-}
{- |
>>> sum (Third3 1 :: Either3 String Bool Int)
1
>>> length (First3 "x" :: Either3 String Bool Int)
0
-}
instance Foldable (Either3 a b) where
foldMap =
foldMapEither3
{-# INLINE foldMap #-}
foldr =
foldrEither3
{-# INLINE foldr #-}
{- |
>>> traverse (\x -> [x, x + 1]) (Third3 1 :: Either3 String Bool Int)
[Third3 1,Third3 2]
>>> traverse (\x -> [x, x + 1]) (Second3 True :: Either3 String Bool Int)
[Second3 True]
-}
instance Traversable (Either3 a b) where
traverse =
traverseEither3
{-# INLINE traverse #-}
{- Fusion
The 'Functor', 'Foldable', 'Traversable', 'Bifunctor', 'Bifoldable' and
'Bitraversable' methods delegate to the functions below, which are not
inlined before phase 1, so that the rewrite rules, which are active before
phase 1, can fuse compositions of them. Each rule is an instance of the functor law
@fmap f . fmap g = fmap (f . g)@, or of the corresponding law for
'bimap', 'foldMap', 'traverse', 'bifoldMap' or 'bitraverse'.
-}
mapEither3 :: (c -> d) -> Either3 a b c -> Either3 a b d
mapEither3 f =
foldEither3 First3 Second3 (Third3 . f)
{-# NOINLINE [1] mapEither3 #-}
bimapEither3 :: (b -> b') -> (c -> c') -> Either3 a b c -> Either3 a b' c'
bimapEither3 f g =
foldEither3 First3 (Second3 . f) (Third3 . g)
{-# NOINLINE [1] bimapEither3 #-}
foldMapEither3 :: (Monoid m) => (c -> m) -> Either3 a b c -> m
foldMapEither3 =
foldEither3 (const mempty) (const mempty)
{-# NOINLINE [1] foldMapEither3 #-}
foldrEither3 :: (c -> x -> x) -> x -> Either3 a b c -> x
foldrEither3 f z =
foldEither3 (const z) (const z) (`f` z)
{-# NOINLINE [1] foldrEither3 #-}
traverseEither3 :: (Applicative g) => (c -> g d) -> Either3 a b c -> g (Either3 a b d)
traverseEither3 f =
foldEither3 (pure . First3) (pure . Second3) (fmap Third3 . f)
{-# NOINLINE [1] traverseEither3 #-}
bifoldMapEither3 :: (Monoid m) => (b -> m) -> (c -> m) -> Either3 a b c -> m
bifoldMapEither3 =
foldEither3 (const mempty)
{-# NOINLINE [1] bifoldMapEither3 #-}
bitraverseEither3 :: (Applicative g) => (b -> g b') -> (c -> g c') -> Either3 a b c -> g (Either3 a b' c')
bitraverseEither3 f g =
foldEither3 (pure . First3) (fmap Second3 . f) (fmap Third3 . g)
{-# NOINLINE [1] bitraverseEither3 #-}
{-# RULES
"Either3 fmap/fmap" [~1] forall f g x.
mapEither3 f (mapEither3 g x) =
mapEither3 (f . g) x
"Either3 bimap/bimap" [~1] forall f g h k x.
bimapEither3 f g (bimapEither3 h k x) =
bimapEither3 (f . h) (g . k) x
"Either3 fmap/bimap" [~1] forall f g h x.
mapEither3 f (bimapEither3 g h x) =
bimapEither3 g (f . h) x
"Either3 bimap/fmap" [~1] forall f g h x.
bimapEither3 f g (mapEither3 h x) =
bimapEither3 f (g . h) x
"Either3 foldMap/fmap" [~1] forall f g x.
foldMapEither3 f (mapEither3 g x) =
foldMapEither3 (f . g) x
"Either3 foldr/fmap" [~1] forall f g z x.
foldrEither3 f z (mapEither3 g x) =
foldrEither3 (f . g) z x
"Either3 traverse/fmap" [~1] forall f g x.
traverseEither3 f (mapEither3 g x) =
traverseEither3 (f . g) x
"Either3 bifoldMap/bimap" [~1] forall f g h k x.
bifoldMapEither3 f g (bimapEither3 h k x) =
bifoldMapEither3 (f . h) (g . k) x
"Either3 bitraverse/bimap" [~1] forall f g h k x.
bitraverseEither3 f g (bimapEither3 h k x) =
bitraverseEither3 (f . h) (g . k) x
#-}
{- |
>>> liftEq2 (==) (==) (Second3 1 :: Either3 String Int Int) (Second3 1)
True
>>> liftEq2 (==) (==) (Second3 1 :: Either3 String Int Int) (Third3 1)
False
-}
instance (Eq a) => Eq2 (Either3 a) where
liftEq2 _ _ (First3 x) (First3 y) = x == y
liftEq2 f _ (Second3 x) (Second3 y) = f x y
liftEq2 _ g (Third3 x) (Third3 y) = g x y
liftEq2 _ _ _ _ = False
{-# INLINE liftEq2 #-}
{- |
>>> liftEq (==) (Third3 1 :: Either3 String Bool Int) (Third3 1)
True
-}
instance (Eq a, Eq b) => Eq1 (Either3 a b) where
liftEq = liftEq2 (==)
{-# INLINE liftEq #-}
{- | Orders 'First3' before 'Second3' before 'Third3', as the derived 'Ord' does.
>>> liftCompare2 compare compare (First3 "x" :: Either3 String Int Int) (Third3 1)
LT
>>> liftCompare2 compare compare (Third3 2 :: Either3 String Int Int) (Third3 1)
GT
-}
instance (Ord a) => Ord2 (Either3 a) where
liftCompare2 _ _ (First3 x) (First3 y) = compare x y
liftCompare2 _ _ (First3 _) _ = LT
liftCompare2 _ _ _ (First3 _) = GT
liftCompare2 f _ (Second3 x) (Second3 y) = f x y
liftCompare2 _ _ (Second3 _) (Third3 _) = LT
liftCompare2 _ _ (Third3 _) (Second3 _) = GT
liftCompare2 _ g (Third3 x) (Third3 y) = g x y
{-# INLINE liftCompare2 #-}
{- |
>>> liftCompare compare (Second3 True :: Either3 String Bool Int) (Third3 1)
LT
-}
instance (Ord a, Ord b) => Ord1 (Either3 a b) where
liftCompare = liftCompare2 compare
{-# INLINE liftCompare #-}
instance (Show a) => Show2 (Either3 a) where
liftShowsPrec2 _ _ _ _ d (First3 x) = showsUnaryWith showsPrec "First3" d x
liftShowsPrec2 sp1 _ _ _ d (Second3 x) = showsUnaryWith sp1 "Second3" d x
liftShowsPrec2 _ _ sp2 _ d (Third3 x) = showsUnaryWith sp2 "Third3" d x
{-# INLINE liftShowsPrec2 #-}
instance (Show a, Show b) => Show1 (Either3 a b) where
liftShowsPrec = liftShowsPrec2 showsPrec showList
{-# INLINE liftShowsPrec #-}
{- | The first 'Third3' wins, otherwise the second value, like 'Either'.
>>> First3 "x" <> Third3 1 :: Either3 String Bool Int
Third3 1
>>> Third3 1 <> Third3 2 :: Either3 String Bool Int
Third3 1
>>> First3 "x" <> Second3 True :: Either3 String Bool Int
Second3 True
-}
instance Semigroup (Either3 a b c) where
(<>) =
(<!>)
{-# INLINE (<>) #-}
{- |
>>> Third3 (+ 1) <.> Third3 1 :: Either3 String Bool Int
Third3 2
>>> First3 "x" <.> Second3 True :: Either3 String Bool Int
First3 "x"
-}
instance Apply (Either3 a b) where
First3 a <.> _ =
First3 a
Second3 b <.> _ =
Second3 b
Third3 f <.> e =
fmap f e
{-# INLINE (<.>) #-}
{- |
>>> pure 1 :: Either3 String Bool Int
Third3 1
>>> Third3 (+ 1) <*> Second3 True :: Either3 String Bool Int
Second3 True
-}
instance Applicative (Either3 a b) where
pure =
Third3
{-# INLINE pure #-}
(<*>) =
(<.>)
{-# INLINE (<*>) #-}
{- |
>>> Third3 1 >>- (\x -> Third3 (x + 1)) :: Either3 String Bool Int
Third3 2
-}
instance Bind (Either3 a b) where
(>>-) =
(>>=)
{-# INLINE (>>-) #-}
{- |
>>> Third3 1 >>= (\x -> if x > 0 then Third3 (x + 1) else Second3 False) :: Either3 String Bool Int
Third3 2
>>> Second3 True >>= (\x -> Third3 (x + 1)) :: Either3 String Bool Int
Second3 True
-}
instance Monad (Either3 a b) where
First3 a >>= _ =
First3 a
Second3 b >>= _ =
Second3 b
Third3 c >>= f =
f c
{-# INLINE (>>=) #-}
{- | Combines two 'Third3' values, otherwise the first 'First3' or 'Second3', as 'Control.Applicative.liftA2' does.
>>> mzip (Third3 1) (Third3 True) :: Either3 String Bool (Int, Bool)
Third3 (1,True)
>>> mzip (Second3 False) (First3 "x") :: Either3 String Bool (Int, Bool)
Second3 False
-}
instance MonadZip (Either3 a b) where
mzipWith =
liftA2
{-# INLINE mzipWith #-}
{- | The first 'Third3' wins, otherwise the second value, like 'Either'.
>>> First3 "x" <!> Third3 1 :: Either3 String Bool Int
Third3 1
>>> Second3 True <!> First3 "x" :: Either3 String Bool Int
First3 "x"
-}
instance Alt (Either3 a b) where
Third3 c <!> _ =
Third3 c
_ <!> e =
e
{-# INLINE (<!>) #-}
{- |
>>> duplicated (Third3 1 :: Either3 String Bool Int)
Third3 (Third3 1)
>>> duplicated (Second3 True :: Either3 String Bool Int)
Second3 True
-}
instance Extend (Either3 a b) where
duplicated (First3 a) =
First3 a
duplicated (Second3 b) =
Second3 b
duplicated w@(Third3 _) =
Third3 w
{-# INLINE duplicated #-}
{- |
>>> select (Third3 (Left 1)) (Third3 (+ 1)) :: Either3 String Bool Int
Third3 2
>>> select (Third3 (Right 1)) (Second3 True) :: Either3 String Bool Int
Third3 1
-}
instance Selective (Either3 a b) where
select =
selectM
{-# INLINE select #-}
{- | Maps the second and third type parameters.
>>> bimap not (+ 1) (Second3 True :: Either3 String Bool Int)
Second3 False
>>> bimap not (+ 1) (Third3 1 :: Either3 String Bool Int)
Third3 2
-}
instance Bifunctor (Either3 a) where
bimap =
bimapEither3
{-# INLINE bimap #-}
{- |
>>> bifoldMap show show (Second3 True :: Either3 String Bool Int)
"True"
>>> bifoldMap show show (First3 "x" :: Either3 String Bool Int)
""
-}
instance Bifoldable (Either3 a) where
bifoldMap =
bifoldMapEither3
{-# INLINE bifoldMap #-}
{- |
>>> bitraverse (\b -> [b, not b]) (\c -> [c]) (Second3 True :: Either3 String Bool Int)
[Second3 True,Second3 False]
-}
instance Bitraversable (Either3 a) where
bitraverse =
bitraverseEither3
{-# INLINE bitraverse #-}
{- | Swaps the second and third type parameters.
>>> swap (Second3 True :: Either3 String Bool Int)
Third3 True
>>> swap (First3 "x" :: Either3 String Bool Int)
First3 "x"
-}
instance Swap (Either3 a) where
swap (First3 a) =
First3 a
swap (Second3 b) =
Third3 b
swap (Third3 c) =
Second3 c
{-# INLINE swap #-}
{- | The index is always @()@, as for 'Maybe'.
>>> imap (\() x -> x + 1) (Third3 1 :: Either3 String Bool Int)
Third3 2
-}
instance FunctorWithIndex () (Either3 a b) where
imap f =
fmap (f ())
{-# INLINE imap #-}
instance FoldableWithIndex () (Either3 a b) where
ifoldMap f =
foldMap (f ())
{-# INLINE ifoldMap #-}
instance TraversableWithIndex () (Either3 a b) where
itraverse f =
traverse (f ())
{-# INLINE itraverse #-}
{- | Traverses the single value, like the 'Each' instance for @Either a a@.
>>> over each (+ 1) (Second3 1 :: Either3 Int Int Int)
Second3 2
>>> toListOf each (First3 1 :: Either3 Int Int Int)
[1]
-}
instance Each (Either3 a a a) (Either3 b b b) a b where
each f (First3 a) =
First3 <$> f a
each f (Second3 a) =
Second3 <$> f a
each f (Third3 a) =
Third3 <$> f a
{-# INLINE each #-}
{- |
>>> (First3 1 :: Either3 Int Bool String) ^? _I1
Just 1
>>> over _I1 show (First3 1 :: Either3 Int Bool String)
First3 "1"
-}
instance Injection1 (Either3 a b c) (Either3 a' b c) a a'
{- |
>>> (Second3 True :: Either3 Int Bool String) ^? _I2
Just True
>>> _I2 # True :: Either3 Int Bool String
Second3 True
-}
instance Injection2 (Either3 a b c) (Either3 a b' c) b b'
{- |
>>> (Third3 "x" :: Either3 Int Bool String) ^? _I3
Just "x"
>>> (First3 1 :: Either3 Int Bool String) ^? _I3
Nothing
-}
instance Injection3 (Either3 a b c) (Either3 a b c') c c'
{- | Class for types that can be viewed as an 'Either3'.
>>> view getEither3 (Third3 1 :: Either3 String Bool Int)
Third3 1
-}
class GetEither3 s a b c | s -> a b c where
getEither3 :: Getter s (Either3 a b c)
instance GetEither3 (Either3 a b c) a b c where
getEither3 = id
{-# INLINE getEither3 #-}
{- | Class for types that have a 'Lens'' to an 'Either3'. Instances define
'setEither3'; the lens 'either3' follows from it and from 'getEither3'.
>>> setEither3 (Second3 True) (Third3 1 :: Either3 String Bool Int)
Second3 True
>>> over either3 (fmap (+ 1)) (Third3 1 :: Either3 String Bool Int)
Third3 2
-}
class (GetEither3 s a b c) => HasEither3 s a b c | s -> a b c where
{-# MINIMAL setEither3 #-}
setEither3 :: Either3 a b c -> s -> s
either3 :: Lens' s (Either3 a b c)
either3 = lens (view getEither3) (flip setEither3)
{-# INLINE either3 #-}
instance HasEither3 (Either3 a b c) a b c where
setEither3 = const
{-# INLINE setEither3 #-}
{- | Class for types that can be constructed from an 'Either3'.
>>> review reviewEither3 (First3 "x") :: Either3 String Bool Int
First3 "x"
-}
class ReviewEither3 t a b c | t -> a b c where
reviewEither3 :: Review t (Either3 a b c)
instance ReviewEither3 (Either3 a b c) a b c where
reviewEither3 = unto id
{-# INLINE reviewEither3 #-}
{- | Class for types that have a 'Prism'' to an 'Either3'. Instances define
'matchEither3'; the prism '_Either3' follows from it and from 'reviewEither3'.
>>> matchEither3 (Third3 1 :: Either3 String Bool Int)
Just (Third3 1)
>>> (Third3 1 :: Either3 String Bool Int) ^? _Either3
Just (Third3 1)
-}
class (ReviewEither3 t a b c) => AsEither3 t a b c | t -> a b c where
{-# MINIMAL matchEither3 #-}
matchEither3 :: t -> Maybe (Either3 a b c)
_Either3 :: Prism' t (Either3 a b c)
_Either3 = prism' (review reviewEither3) matchEither3
{-# INLINE _Either3 #-}
instance AsEither3 (Either3 a b c) a b c where
matchEither3 = Just
{-# INLINE matchEither3 #-}