effet-0.1.0.0: src/Control/Effect/Reader.hs
{-# LANGUAGE TemplateHaskell #-}
-----------------------------------------------------------------------------
-- |
-- Module : Control.Effect.Reader
-- Copyright : (c) Michael Szvetits, 2020
-- License : BSD3 (see the file LICENSE)
-- Maintainer : typedbyte@qualified.name
-- Stability : stable
-- Portability : portable
--
-- The reader effect, similar to the @MonadReader@ type class from the @mtl@
-- library.
-----------------------------------------------------------------------------
module Control.Effect.Reader
( -- * Tagged Reader Effect
Reader'(..)
-- * Untagged Reader Effect
-- | If you don't require disambiguation of multiple reader effects
-- (i.e., you only have one reader effect in your monadic context),
-- it is recommended to always use the untagged reader effect.
, Reader
, ask
, local
, reader
-- * Convenience Functions
-- | If you don't require disambiguation of multiple reader effects
-- (i.e., you only have one reader effect in your monadic context),
-- it is recommended to always use the untagged functions.
, asks'
, asks
-- * Interpretations
, runReader'
, runReader
-- * Tagging and Untagging
-- | Conversion functions between the tagged and untagged reader effect,
-- usually used in combination with type applications, like:
--
-- @
-- 'tagReader'' \@\"newTag\" program
-- 'retagReader'' \@\"oldTag\" \@\"newTag\" program
-- 'untagReader'' \@\"erasedTag\" program
-- @
--
, tagReader'
, retagReader'
, untagReader'
) where
-- transformers
import qualified Control.Monad.Trans.Reader as R
import Control.Effect.Machinery (G, Tagger(Tagger), Via, makeTaggedEffect, runVia)
-- | An effect that adds an immutable state (i.e., an \"environment\") to a given
-- computation. The effect allows to read values from the environment, pass values
-- from function to function, and execute sub-computations in a modified environment.
class Monad m => Reader' tag r m | tag m -> r where
{-# MINIMAL (ask' | reader'), local' #-}
-- | Gets the environment.
ask' :: m r
ask' = reader' @tag id
{-# INLINE ask' #-}
-- | Executes a sub-computation in a modified environment.
local' :: (r -> r) -- ^ The function to modify the environment.
-> m a -- ^ The sub-computation to run in the modified environment.
-> m a -- ^ The result of the sub-computation.
-- | Gets a specific component of the environment, using the provided projection function.
reader' :: (r -> a) -- ^ The projection function to apply to the environment.
-> m a -- ^ The result of the projection.
reader' f = do
r <- ask' @tag
pure (f r)
{-# INLINE reader' #-}
makeTaggedEffect ''Reader'
instance Monad m => Reader' tag r (R.ReaderT r m) where
ask' = R.ask
{-# INLINE ask' #-}
local' = R.local
{-# INLINE local' #-}
reader' = R.reader
{-# INLINE reader' #-}
-- | Gets a specific component of the environment, using the provided projection function.
asks' :: forall tag r m a. Reader' tag r m
=> (r -> a) -- ^ The projection function to apply to the environment.
-> m a -- ^ The result of the projection.
asks' = reader' @tag
{-# INLINE asks' #-}
-- | The untagged version of 'asks''.
asks :: Reader r m => (r -> a) -> m a
asks = asks' @G
{-# INLINE asks #-}
-- | Runs the reader effect.
runReader' :: forall tag r m a. r -- ^ The initial environment.
-> (Reader' tag r `Via` R.ReaderT r) m a -- ^ The program whose reader effect should be handled.
-> m a -- ^ The program with its reader effect handled.
runReader' r = flip R.runReaderT r . runVia
{-# INLINE runReader' #-}
-- | The untagged version of 'runReader''.
runReader :: r -> (Reader r `Via` R.ReaderT r) m a -> m a
runReader = runReader' @G
{-# INLINE runReader #-}