Safe Haskell | None |
---|---|
Language | Haskell2010 |
Elevator
Elevator
can be used to lift instances through monad transformers as long as they implement
a MonadTrans
/ MonadTransControl
/ MonadTransControlIdentity
instance.
MonadTransControl
is only necessary when there is atleast one method with a monadic argument.
MonadTransControlIdentity
is even stronger and only required for a few specific instances.
newtype Elevator t m a Source #
A newtype wrapper for monad transformers.
Access instances of the inner monad m
.
Type level arguments:
Instances
Examples
Example 1: Recover submerged instances
Let's assume you want to define a monad transformer stack.
newtype StackT m a = StackT { unStackT ::ReaderT
Char
(ReaderT
Bool
m) a } deriving newtype (Functor
,Applicative
,Monad
)
Now you want to expose the inner (
instance with MonadReader
Bool
)(StackT m)
.
Normally it's shadowed by the (
instance, but we can use MonadReader
Char
)Elevator
to
access the inner transformer.
deriving (MonadReader
Bool
) viaElevator
(ReaderT
Char
) (ReaderT
Bool
m)
Example 2: Custom transformer without boilerplate
Let's assume you have defined a monad transformer.
newtype CustomT m a = CustomT { unCustomT ::IdentityT
m a } deriving newtype (Functor
,Applicative
,Monad
) deriving newtype (MonadTrans
,MonadTransControl
) runCustomT :: CustomT m a -> m a runCustomT =runIdentityT
. unCustomT
Now you want to use this monad transformer in a transformer stack.
newtype StackT m a = StackT { unStackT :: CustomT (ReaderT
Bool
m) a } deriving newtype (Functor
,Applicative
,Monad
)
Unfortunately we can't derive a (
instance with
GeneralizedNewtypeDeriving, without also adding the instance to Monad
m => MonadReader
Bool
(StackT m))CustomT
.
To still derive this trivial instance we can use Elevator
with DerivingVia.
deriving (MonadReader
Bool
) via (Elevator
CustomT (ReaderT
Bool
m))
Example 3: Adding an instance for Elevator
Suppose you define a new type class.
class Monad
m => MonadCustom m where
simpleMethod :: a -> m a
complicatedMethod :: (a -> m b) -> m b
A simple way to allow a type class to be lifted through other monad transformers is by adding an
instance for Elevator
.
You have to be careful about monadic state StT
, when defining such instances using
MonadTransControl
.
instance (MonadCustom m,MonadTransControl
t) => MonadCustom (Elevator
t m) where simpleMethod =lift
. simpleMethod complicatedMethod f = (restoreT
.pure
=<<
) $liftWith
$ \ runT -> complicatedMethod $ runT . f
Some useful examples (or exercises) are the instances for
mtl's type classes (MonadError
, MonadReader
,
MonadState
, MonadWriter
).