effects-0.2.2: src/Control/Effects.hs
{-# LANGUAGE MultiParamTypeClasses, KindSignatures, ScopedTypeVariables, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}
module Control.Effects (
-- * Running effects
-- $rundoc
with
, run
-- * Defining effects
-- $defdoc
, Handler(..)
, operation
-- * Base monad
-- $basedoc
, runBase
, base
-- * Effects machinery
-- $macdoc
, Layer(..)
, Base(..)
, Pure(..)
, Effect
, AutoLift
, AutoLiftBase
) where
import Control.Applicative
import Control.Monad
import Data.Monoid
-- $rundoc
-- Here's an example how to use the state effect from 'Control.Effects.State':
--
-- > example :: Int
-- > example = run $ do
-- > with (ref 10) $ \u -> do
-- > val <- get u
-- > put u (val + 5)
-- > get u
-- | @with@ takes a handler and creates a new effect instance.
-- The @Effect@ is passed on to a function which can use it to do operations with it.
with :: Monad m => Handler e r m a -> (Effect e m -> Layer e m a) -> m r
with h f = runLayer (f Effect) (ret h) >>= fin h
-- | Unwrap the result of the top-level effect.
run :: Base Pure a -> a
run (Base (Pure a)) = a
-- $defdoc
-- Here's and example how to define the state effect from 'Control.Effects.Writer':
--
-- > writer :: (Monad m, Monoid w) => Handler (w, a) (w, a) m a
-- > writer = Handler
-- > { ret = \a -> return (mempty, a)
-- > , fin = return
-- > }
-- >
-- > tell :: (AutoLift (w, r) m n, Monoid w) => Effect (w, r) m -> w -> n ()
-- > tell p v = operation p $ \k -> do
-- > (w, r) <- k ()
-- > return (mappend v w, r)
-- | A @Handler e r m a@ is a handler of effects with type @e@.
-- The @ret@ field provides a function to lift pure values into the effect.
-- The @fin@ field provides a function to extract a final value of type @r@ from the effect.
-- The parameter @m@ should normally be left polymorphic, it's the monad that handles the other effects.
data Handler e r m a = Handler
{ ret :: a -> m e
, fin :: e -> m r
}
-- | @operation@ takes an effect identifier generated by `with` and a function which takes a continuation as parameter.
-- The result is auto-lifted so it can be used inside any other effect.
operation :: AutoLift e m n => Effect e m -> ((a -> m e) -> m e) -> n a
operation = operation'
-- $basedoc
-- The effects are layered on top of a base monad. Here's an example how to use `IO` as a base monad:
--
-- > exampleIO :: IO ()
-- > exampleIO = runBase $ do
-- > with (ref 5) $ \x -> do
-- > val <- get x
-- > base $ print val
-- | @base@ takes a computation in the base monad and auto-lifts it so it can be used inside any effect.
base :: AutoLiftBase m n => m a -> n a
base = base'
-- | Unwrap the result of a computation using a base monad.
runBase :: Base m a -> m a
runBase (Base m) = m
-- $macdoc
-- Effects are layered in a stack on top of a base monad. Just like with monad transformers, operations lower in the stack
-- need to be lifted to be able to be used together with operations higher in the stack. But as there are only two monads
-- in play, `Layer` and `Base`, and because each operation is identified with exactly one layer using the `Effect` type,
-- lifting can be done automatically.
--
-- The following types and classes show up in the type signatures. The compiler should be able to infer them for you.
-- | @Layer e m@ is a monad that adds an effect @e@ to the underlying monad @m@.
-- (It is the continuation monad transformer with a friendlier name.)
newtype Layer e m a = Layer { runLayer :: (a -> m e) -> m e }
instance Functor (Layer e m) where
fmap f m = Layer $ \k -> runLayer m (k . f)
instance Applicative (Layer e m) where
pure a = Layer $ \k -> k a
m <*> v = Layer $ \k -> runLayer m (\f -> runLayer v (k . f))
instance (Monoid e, Applicative m) => Alternative (Layer e m) where
empty = Layer $ \_ -> pure mempty
l <|> r = Layer $ \k -> mappend <$> runLayer l k <*> runLayer r k
instance Monad (Layer e m) where
return a = Layer $ \k -> k a
m >>= f = Layer $ \k -> runLayer m (\a -> runLayer (f a) k)
instance (Monoid e, Applicative m) => MonadPlus (Layer e m) where
mzero = empty
mplus = (<|>)
-- | @Pure@ is the identity monad and is used when no other base monad is needed.
newtype Pure a = Pure a
instance Functor Pure where
fmap f (Pure a) = Pure (f a)
instance Applicative Pure where
pure = Pure
Pure f <*> Pure a = Pure (f a)
instance Monad Pure where
return = Pure
Pure a >>= f = f a
-- | @Base m@ is a newtype wrapper around a monadic computation.
newtype Base m a = Base (m a)
instance Functor m => Functor (Base m) where
fmap f (Base m) = Base (fmap f m)
instance Applicative m => Applicative (Base m) where
pure = Base . pure
Base m <*> Base v = Base (m <*> v)
instance Monad m => Monad (Base m) where
return = Base . return
Base m >>= f = Base $ m >>= runBase . f
-- | @Effect e m@ is a proxy for the type checker to be able to work with multiple effects at the same time.
data Effect e (m :: * -> *) = Effect
class (Applicative m, Applicative n, Monad m, Monad n) => AutoLift e m n where
operation' :: Effect e m -> ((a -> m e) -> m e) -> n a
instance (Applicative m, Applicative n, Monad m, Monad n, AutoLiftInternal (Layer e m) (Base n) (Layer e m) (Base n)) => AutoLift e m (Base n) where
operation' _ f = autolift (Proxy :: Proxy (Layer e m)) (Proxy :: Proxy (Base n)) (Layer f)
instance (Applicative m, Applicative n, Monad m, Monad n, AutoLiftInternal (Layer e m) (Layer d n) (Layer e m) (Layer d n)) => AutoLift e m (Layer d n) where
operation' _ f = autolift (Proxy :: Proxy (Layer e m)) (Proxy :: Proxy (Layer d n)) (Layer f)
class (Applicative m, Applicative n, Monad m, Monad n) => AutoLiftBase m n where
base' :: m a -> n a
instance (Applicative m, Applicative n, Monad m, Monad n, AutoLiftInternal (Base m) (Base n) (Base m) (Base n)) => AutoLiftBase m (Base n) where
base' m = autolift (Proxy :: Proxy (Base m)) (Proxy :: Proxy (Base n)) (Base m)
instance (Applicative m, Applicative n, Monad m, Monad n, AutoLiftInternal (Base m) (Layer e n) (Base m) (Layer e n)) => AutoLiftBase m (Layer e n) where
base' m = autolift (Proxy :: Proxy (Base m)) (Proxy :: Proxy (Layer e n)) (Base m)
data Proxy (m :: * -> *) = Proxy
class (Applicative m1, Applicative m2, Monad m1, Monad m2) => AutoLiftInternal m1 m2 n1 n2 where
autolift :: Proxy n1 -> Proxy n2 -> m1 a -> m2 a
pre :: Proxy (Layer r m) -> Proxy m
pre Proxy = Proxy
instance (Applicative m, Monad m) => AutoLiftInternal m m (Base n) (Base n) where
autolift Proxy Proxy = id
instance (AutoLiftInternal m1 m2 (Base n1) n2) => AutoLiftInternal m1 (Layer r m2) (Base n1) (Layer s n2) where
autolift p1 p2 = Layer . (>>=) . autolift p1 (pre p2)
instance (AutoLiftInternal m1 m2 n1 n2) => AutoLiftInternal m1 m2 (Layer r n1) (Layer s n2) where
autolift p1 p2 = autolift (pre p1) (pre p2)