Skip to content

Instantly share code, notes, and snippets.

@plcplc
Created July 9, 2021 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plcplc/237df4c8477b7656fef9155cf4dcd19e to your computer and use it in GitHub Desktop.
Save plcplc/237df4c8477b7656fef9155cf4dcd19e to your computer and use it in GitHub Desktop.
Let's put some effects in your effects!
module EffectsInEffects where
import Polysemy as Poly
-- Plumbing:
class Monad m => EffectMonad m where
type R m :: EffectRow
wrapEffectMonad :: Sem (R m) a -> m a
unwrapEffectMonad :: m a -> Sem (R m) a
class Monad m => ReaderEffect r m where
ask :: m r
local :: (r -> r) -> m a -> m a
instance
(EffectMonad m, Member (Poly.Reader r) (R m)) =>
ReaderEffect r m
where
ask = wrapEffectMonad $ Poly.ask @r
local f m = wrapEffectMonad $ Poly.local f (unwrapEffectMonad m)
-- Application design pattern
newtype App a = App {runApp :: Sem AppStack a}
deriving (Functor, Applicative, Monad)
type AppStack = [ Reader (App Int) ]
instance EffectMonad App where
type R App = AppStack
wrapEffectMonad = App
unwrapEffectMonad = runApp
someAppAction :: forall m. ReaderEffect (m Int) m => m Int
someAppAction = do
act <- ask @(m Int)
x <- act
return (x+2)
-- In summary:
-- * We're back to using only type class constraints in our application logic
-- without carrying around our effect stack in a type variable.
-- * We don't have the instance overhead of MTL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment