packages feed

ribosome-0.2.0.0: lib/Ribosome/Control/Monad/Ribo.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FunctionalDependencies #-}

module Ribosome.Control.Monad.Ribo(
  Ribo,
  RiboT(..),
  MonadRibo(..),
  MonadRiboError(..),
  unsafeToNeovim,
) where

import Control.Concurrent.STM.TVar (swapTVar)
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.State (MonadState(..))
import Control.Monad.Trans.Except (ExceptT(ExceptT), runExceptT, mapExceptT)
import qualified Control.Monad.Trans.Except as Except (catchE)
import Data.Bifunctor (Bifunctor(..))
import Data.Either.Combinators (mapLeft)
import Data.Functor (void)
import UnliftIO.Exception (throwString)
import UnliftIO.STM (TVar, atomically, readTVarIO)
import Neovim (ask)
import Neovim.Context.Internal (Neovim(Neovim))
import Ribosome.Control.Ribosome (Ribosome)
import qualified Ribosome.Control.Ribosome as Ribosome (env)

type Ribo e = Neovim (Ribosome e)

newtype RiboT s e a =
  RiboT { unRiboT :: ExceptT e (Neovim (Ribosome (TVar s))) a }
  deriving (Functor, Applicative, Monad)

instance Bifunctor (RiboT s) where
  first f (RiboT r) =
    RiboT $ mapExceptT (fmap $ mapLeft f) r

  second = fmap

class MonadRibo s e m | m -> s, m -> e where
  nvim :: Neovim (Ribosome (TVar s)) a -> m a
  asNeovim :: m a -> Neovim (Ribosome (TVar s)) (Either e a)

class Monad (t e) => MonadRiboError e t where
  liftEither :: Either e a -> t e a
  mapE :: (e -> e') -> t e a -> t e' a
  catchE :: (e -> t e' a) -> t e a -> t e' a

instance MonadRibo s e (RiboT s e) where
  nvim = RiboT . ExceptT . fmap Right
  asNeovim = runExceptT . unRiboT

instance MonadRiboError e (RiboT s) where
  liftEither = RiboT . ExceptT . return
  mapE f = RiboT . mapExceptT (fmap $ mapLeft f) . unRiboT
  catchE f = RiboT . flip Except.catchE (unRiboT . f) . unRiboT

instance MonadError e (RiboT s e) where
  throwError = liftEither . Left
  catchError = flip catchE

stateTVar :: (Functor m, MonadRibo s e m) => m (TVar s)
stateTVar =
  Ribosome.env <$> nvim ask

instance MonadState s (RiboT s e) where
  get = do
    t <- stateTVar
    nvim $ readTVarIO t
  put newState = do
    t <- stateTVar
    void $ nvim $ atomically $ swapTVar t newState

unsafeToNeovim :: (MonadRibo s e m, Show e) => m a -> Neovim (Ribosome (TVar s)) a
unsafeToNeovim ra = do
  r <- asNeovim ra
  either (throwString . show) return r