alternators-1.0.0.0: src/Control/Also.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
module Control.Also where
import Control.Applicative
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.Cont
import Control.Monad.Trans.Except
import Control.Monad.Trans.Identity
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Reader
import Control.Monad.Trans.RWS.Lazy as Lazy
import Control.Monad.Trans.RWS.Strict as Strict
import Control.Monad.Trans.State.Lazy as Lazy
import Control.Monad.Trans.State.Strict as Strict
import Control.Monad.Trans.Writer.Lazy as Lazy
import Control.Monad.Trans.Writer.Strict as Strict
import Control.Newtype.Generics
import Data.Functor.Identity
import GHC.Generics
#if MIN_VERSION_base(4,9,0) && !MIN_VERSION_base(4,10,0)
import Data.Semigroup
#endif
-- | Combining effects where both input effects are used as much as possible.
-- as opposed to 'Control.Applicative.Alternative' where only the "successful" effect is used.
class Also f a where
-- | An associative binary operation, where both input effects are used as much as possible.
also :: f a -> f a -> f a
-- | The identity of 'also'
alsoZero :: f a
infixr 6 `also` -- like <>
-- | Monoid under 'also'.
-- Mnemonic: 'Als' for 'Also', just like 'Alt' for 'Altenative'
newtype Als f a = Als { getAls :: f a }
deriving (Generic, Generic1, Read, Show, Eq, Ord, Num, Enum,
Monad, MonadPlus, Applicative, Alternative, Functor)
instance Newtype (Als f a)
instance Also f a => Semigroup (Als f a) where
(Als f) <> (Als g) = Als (f `also` g)
instance Also f a => Monoid (Als f a) where
mempty = Als alsoZero
#if !MIN_VERSION_base(4,11,0)
(Als f) `mappend` (Als g) = Als (f `also` g)
#endif
-- | Overlappable instance for all Applicatives of Monoids.
#if MIN_VERSION_base(4,11,0)
instance {-# OVERLAPPABLE #-} (Monoid a, Applicative f) => Also f a where
alsoZero = pure mempty
f `also` g = liftA2 (<>) f g
#else
instance {-# OVERLAPPABLE #-} (Monoid a, Applicative f) => Also f a where
alsoZero = pure mempty
f `also` g = liftA2 mappend f g
#endif
#if MIN_VERSION_base(4,11,0)
instance (Monoid a) => Also Identity a where
alsoZero = mempty
a `also` b = a <> b
#else
instance (Monoid a) => Also Identity a where
alsoZero = mempty
a `also` b = a `mappend` b
#endif
#if MIN_VERSION_base(4,11,0)
instance (Monoid a) => Also IO a where
alsoZero = mempty
a `also` b = a <> b
#else
instance (Monoid a) => Also IO a where
alsoZero = pure mempty
a `also` b = liftA2 mappend a b
#endif
instance (Also m a) => Also (IdentityT m) a where
alsoZero = IdentityT alsoZero
(IdentityT a) `also` (IdentityT b) = IdentityT $ a `also` b
-- | Combine the monads that returns @r@ not @a@.
instance (Also m r) => Also (ContT r m) a where
alsoZero = ContT . const $ alsoZero
(ContT f) `also` (ContT g) =
ContT $ \k -> (f k) `also` (g k)
instance (Also m a) => Also (ReaderT r m) a where
alsoZero = ReaderT $ const alsoZero
(ReaderT f) `also` (ReaderT g) = ReaderT $ \r -> f r `also` g r
instance (Also m (Either e a)) => Also (ExceptT e m) a where
alsoZero = ExceptT $ alsoZero
(ExceptT f) `also` (ExceptT g) = ExceptT $ f `also` g
instance (Also m (Maybe a)) => Also (MaybeT m) a where
alsoZero = MaybeT $ alsoZero
(MaybeT f) `also` (MaybeT g) = MaybeT $ f `also` g
-- | State instances threads the state through both monad of 'also'
-- in the normal left to right order, and so do not prevent
-- early termination from the left monad (eg if the inner monad was
-- a 'MaybeT' or 'ExceptT'.
-- However, it is able to use the 'also' to combine the return value.
instance (Also m a, Monad m) => Also (Lazy.StateT s m) a where
alsoZero = lift alsoZero
f `also` g = do
(x, y) <- liftA2 (,) f g
lift $ pure x `also` pure y
-- | State instances threads the state through both monad of 'also'
-- in the normal left to right order, and so do not prevent
-- early termination from the left monad (eg if the inner monad was
-- a 'MaybeT' or 'ExceptT'.
-- However, it is able to use the 'also' to combine the return value.
instance (Also m a, Monad m) => Also (Strict.StateT s m) a where
alsoZero = lift alsoZero
f `also` g = do
(x, y) <- liftA2 (,) f g
lift $ pure x `also` pure y
-- | Writer instances threads the writer through both monad of 'also'
-- in the normal left to right order, and so do not prevent
-- early termination from the left monad (eg if the inner monad was
-- a 'MaybeT' or 'ExceptT'.
-- However, it is able to use the 'also' to combine the return value.
instance (Monoid w, Also m a, Monad m) => Also (Lazy.WriterT w m) a where
alsoZero = lift alsoZero
f `also` g = do
(x, y) <- liftA2 (,) f g
lift $ pure x `also` pure y
-- | Writer instances threads the writer through both monad of 'also'
-- in the normal left to right order, and so do not prevent
-- early termination from the left monad (eg if the inner monad was
-- a 'MaybeT' or 'ExceptT'.
-- However, it is able to use the 'also' to combine the return value.
instance (Monoid w, Also m a, Monad m) => Also (Strict.WriterT w m) a where
alsoZero = lift alsoZero
f `also` g = do
(x, y) <- liftA2 (,) f g
lift $ pure x `also` pure y
-- | State instances threads the state through both monad of 'also'
-- in the normal left to right order, and so do not prevent
-- early termination from the left monad (eg if the inner monad was
-- a 'MaybeT' or 'ExceptT'.
-- However, it is able to use the 'also' to combine the return value.
instance (Monoid w, Also m a, Monad m) => Also (Lazy.RWST r w s m) a where
alsoZero = lift alsoZero
f `also` g = do
(x, y) <- liftA2 (,) f g
lift $ pure x `also` pure y
-- | State instances threads the state through both monad of 'also'
-- in the normal left to right order, and so do not prevent
-- early termination from the left monad (eg if the inner monad was
-- a 'MaybeT' or 'ExceptT'.
-- However, it is able to use the 'also' to combine the return value.
instance (Monoid w, Also m a, Monad m) => Also (Strict.RWST r w s m) a where
alsoZero = lift alsoZero
f `also` g = do
(x, y) <- liftA2 (,) f g
lift $ pure x `also` pure y