packages feed

stock-0.1.0.0: plugin/Stock/Trans.hs

{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE BlockArguments #-}
-- | A two-screen slice of @transformers@: the three monad transformers the
-- plugin uses (@ReaderT@, strict @WriterT@, @MaybeT@), inlined so the library
-- depends only on @base@ and @ghc@.  They are used /only/ as @DerivingVia@
-- targets (the synthesis monad in "Stock.Derive", the first-success 'Monoid' in
-- "Stock.Internal"), so the representations match @transformers@ exactly — that
-- is what lets the @via@ coercions go through — and none of the combinators
-- (@lift@, @ask@, @tell@, @runReaderT@, …) are needed beyond the constructors.
module Stock.Trans
  ( ReaderT(..)
  , WriterT(..)
  , MaybeT(..)
  ) where

import Control.Applicative (Alternative(..))
import Control.Monad (ap)
-- liftA2 comes from Prelude (base >= 4.18 / GHC >= 9.6)

-- | @r -> m a@, exactly as in @Control.Monad.Trans.Reader@.
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }

-- | @m (a, w)@ — the /strict/ writer (value first), as in
-- @Control.Monad.Trans.Writer.Strict@.
newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }

-- | @m (Maybe a)@, exactly as in @Control.Monad.Trans.Maybe@.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

instance Functor m => Functor (ReaderT r m) where
  fmap :: (a -> b) -> ReaderT r m a -> ReaderT r m b
  fmap f (ReaderT g) = ReaderT (fmap f . g)

instance Applicative m => Applicative (ReaderT r m) where
  pure :: a -> ReaderT r m a
  pure = ReaderT . const . pure
  (<*>) :: ReaderT r m (a -> b) -> ReaderT r m a -> ReaderT r m b
  ReaderT f <*> ReaderT x = ReaderT \r -> f r <*> x r

instance Monad m => Monad (ReaderT r m) where
  (>>=) :: ReaderT r m a -> (a -> ReaderT r m b) -> ReaderT r m b
  ReaderT x >>= k = ReaderT \r -> x r >>= \a -> runReaderT (k a) r

instance Functor m => Functor (WriterT w m) where
  fmap :: (a -> b) -> WriterT w m a -> WriterT w m b
  fmap f (WriterT m) = WriterT (fmap (\(a, w) -> (f a, w)) m)

instance (Monoid w, Applicative m) => Applicative (WriterT w m) where
  pure :: a -> WriterT w m a
  pure a = WriterT (pure (a, mempty))
  (<*>) :: WriterT w m (a -> b) -> WriterT w m a -> WriterT w m b
  WriterT mf <*> WriterT mx = WriterT (liftA2 k mf mx)
    where k (f, w) (x, w') = (f x, w <> w')

instance (Monoid w, Monad m) => Monad (WriterT w m) where
  (>>=) :: WriterT w m a -> (a -> WriterT w m b) -> WriterT w m b
  WriterT m >>= k = WriterT do
    (a, w)  <- m
    (b, w') <- runWriterT (k a)
    pure (b, w <> w')

instance Functor m => Functor (MaybeT m) where
  fmap :: (a -> b) -> MaybeT m a -> MaybeT m b
  fmap f (MaybeT m) = MaybeT (fmap (fmap f) m)

instance Monad m => Applicative (MaybeT m) where
  pure :: a -> MaybeT m a
  pure = MaybeT . pure . Just
  (<*>) :: MaybeT m (a -> b) -> MaybeT m a -> MaybeT m b
  (<*>) = ap

instance Monad m => Monad (MaybeT m) where
  (>>=) :: MaybeT m a -> (a -> MaybeT m b) -> MaybeT m b
  MaybeT m >>= k = MaybeT do
    ma <- m
    case ma of
      Nothing -> pure Nothing
      Just a  -> runMaybeT (k a)

instance Monad m => Alternative (MaybeT m) where
  empty :: MaybeT m a
  empty = MaybeT (pure Nothing)
  (<|>) :: MaybeT m a -> MaybeT m a -> MaybeT m a
  MaybeT a <|> MaybeT b = MaybeT do
    ma <- a
    case ma of
      Nothing -> b
      Just _  -> pure ma