packages feed

keter-2.1.0: src/Keter/Context.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DerivingStrategies         #-}

module Keter.Context where

import           Keter.Common
import           Control.Monad.Trans       (lift)
import           Control.Monad.IO.Class   (MonadIO)
import           Control.Monad.IO.Unlift   (MonadUnliftIO)
import           Control.Monad.Logger      (MonadLogger, MonadLoggerIO, LoggingT(..), runLoggingT)
import           Control.Monad.Reader      (MonadReader, ReaderT, runReaderT, ask
                                           , withReaderT)

-- | The top-level keter context monad, carrying around the main logger and some locally relevant configuration structure.
--
-- See this blog post for an explanation of the design philosophy: https://www.fpcomplete.com/blog/2017/06/readert-design-pattern/
--
-- TODO: generalize as /contexts/ instead of /configs/? Since not every state being passed
-- around can be intuitively thought of as a config per se. Ex. AppManager
newtype KeterM cfg a = KeterM { runKeterM :: LoggingT (ReaderT cfg IO) a }
  deriving newtype (Functor, Applicative, Monad,
                    MonadUnliftIO,
                    MonadIO, MonadLogger, MonadLoggerIO,
                    MonadReader cfg)

withMappedConfig :: (cfg -> cfg') -> KeterM cfg' a -> KeterM cfg a
withMappedConfig f (KeterM ctx) = 
    KeterM $ LoggingT $ \logger -> withReaderT f $ runLoggingT ctx logger