packages feed

bowtie-0.3.0: src/Bowtie/Rewrite.hs

{-# LANGUAGE UndecidableInstances #-}

module Bowtie.Rewrite where

import Bowtie (Jot, pattern JotP)
import Control.Exception (Exception)
import Control.Monad ((>=>))
import Control.Monad.Except (ExceptT (..), MonadError (..), runExceptT)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Identity (Identity (..))
import Control.Monad.Reader (MonadReader (..), ReaderT (..), asks)
import Control.Monad.State (MonadState (..))
import Control.Monad.Trans (MonadTrans (..))
import Data.Bitraversable (Bitraversable (..))
import Data.Sequence.NonEmpty (NESeq)
import Data.Sequence.NonEmpty qualified as NESeq
import Data.Typeable (Typeable)
import Data.Void (Void, absurd)

data AnnoErr k e = AnnoErr
  { annoErrKey :: !k
  , annoErrVal :: !e
  }
  deriving stock (Eq, Ord, Show)

instance
  (Show k, Typeable k, Show e, Typeable e)
  => Exception (AnnoErr k e)

unwrapAnnoErr :: Either (AnnoErr k Void) a -> a
unwrapAnnoErr = either (absurd . annoErrVal) id

newtype RwT k e m a = RwT {unRwT :: ReaderT (NESeq k) (ExceptT (AnnoErr k e) m) a}
  deriving newtype (Functor, Applicative, Monad)

type Rw k e = RwT k e Identity

instance MonadTrans (RwT k e) where
  lift = RwT . lift . lift

runRwT :: RwT k e m a -> k -> m (Either (AnnoErr k e) a)
runRwT m = runExceptT . runReaderT (unRwT m) . NESeq.singleton

runRw :: Rw k e a -> k -> Either (AnnoErr k e) a
runRw m = runIdentity . runRwT m

embedRwT :: m (Either (AnnoErr k e) a) -> RwT k e m a
embedRwT n = RwT (ReaderT (const (ExceptT n)))

pushRw :: (Monad m) => k -> RwT k e m a -> RwT k e m a
pushRw b m = RwT (local (NESeq.|> b) (unRwT m))

peekRw :: (Monad m) => RwT k e m k
peekRw = RwT (asks NESeq.last)

peeksRw :: (Monad m) => (k -> a) -> RwT k e m a
peeksRw f = RwT (asks (f . NESeq.last))

askRw :: (Monad m) => RwT k e m (NESeq k)
askRw = RwT ask

asksRw :: (Monad m) => (NESeq k -> a) -> RwT k e m a
asksRw f = RwT (asks f)

throwRw :: (Monad m) => e -> RwT k e m a
throwRw e = RwT (asks NESeq.last >>= \b -> throwError (AnnoErr b e))

instance (MonadReader r m) => MonadReader r (RwT k e m) where
  ask = lift ask
  reader f = lift (reader f)
  local f m = RwT $ do
    bs <- ask
    ea <- lift (lift (local f (runExceptT (runReaderT (unRwT m) bs))))
    either throwError pure ea

instance (MonadState s m) => MonadState s (RwT k e m) where
  get = lift get
  put = lift . put
  state f = lift (state f)

instance (MonadIO m) => MonadIO (RwT k e m) where
  liftIO = lift . liftIO

wrapRw :: g a (Jot g k a) -> Rw k e (Jot g k a)
wrapRw = peeksRw . flip JotP

jotRw :: (Bitraversable g) => (g a z -> Rw k e z) -> Jot g k a -> Either (AnnoErr k e) z
jotRw f = runIdentity . jotRwT (bitraverse pure id >=> f)

jotRwT :: (Monad m, Bitraversable g) => (g a (RwT k e m z) -> RwT k e m z) -> Jot g k a -> m (Either (AnnoErr k e) z)
jotRwT f j0@(JotP b0 _) = runRwT (go j0) b0 where go (JotP b g) = pushRw b (f (fmap go g))