content: {-# LANGUAGE Trustworthy #-}
  -- |
  -- Copyright: (c) 2021 Xy Ren
  -- License: BSD3
  -- Maintainer: xy.r@outlook.com
  -- Stability: experimental
  -- Portability: non-portable (GHC only)
  module Cleff.Reader
    ( -- * Effect
      Reader (..)
      -- * Operations
    , ask
    , local
    , asks
      -- * Interpretations
    , runReader
    , magnify
    ) where
  
  import           Cleff
  import           Lens.Micro (Lens', (%~), (&), (^.))
  
  -- * Effect
  
  -- | An effect capable of providing an immutable environment @r@ that can be read. This roughly corresponds to the
  -- @MonadReader@ typeclass and @ReaderT@ monad transformer in the @mtl@ library.
  data Reader r :: Effect where
    Ask :: Reader r m r
    Local :: (r -> r) -> m a -> Reader r m a
  
  -- * Operations
  
  makeEffect_ ''Reader
  
  -- | Obtain the environment value.
  ask :: Reader r :> es => Eff es r
  
  -- | Modify the environment value temporarily for a computation.
  local :: Reader r :> es
    => (r -> r) -- ^ The function that modifies the environment
    -> Eff es a -- ^ The computation to run with the modified environment
    -> Eff es a
  
  -- | Apply a function to the result of 'ask'.
  asks :: Reader r :> es => (r -> s) -> Eff es s
  asks = (<$> ask)
  
  -- * Interpretations
  
  -- | Run a 'Reader' effect with a given environment value.
  runReader :: r -> Eff (Reader r : es) ~> Eff es
  runReader x = interpret (handle x)
    where
      handle :: r -> Handler (Reader r) es
      handle r = \case
        Ask       -> pure r
        Local f m -> toEffWith (handle (f r)) m
  
  -- | Run a 'Reader' effect in terms of a larger 'Reader' via a 'Lens''.
  magnify :: Reader t :> es => Lens' t r -> Eff (Reader r : es) ~> Eff es
  magnify field = interpret \case
    Ask       -> asks (^. field)
    Local f m -> local (& field %~ f) $ toEff m
  