Reputation: 6152
Given a nesting of transformers :: T2 of T1 of M0
, how can I utilize :: T2 of M0
, :: M2
?
Here's an example: I am writing some function which requires reading, logging, and state. The definition:
gameRoutine :: WriterT [String] (ReaderT Env (State Viable)) NUM
If I want to call some
stateFunction :: State Viable NUM
Or even stateWithReaderFunction :: ReaderT Env (State Viable) NUM
,
I can use lift
:
gameRoutine = do
x <- lift . lift $ stateFunction
y <- lift $ stateWithReaderFunction
But how do I call writerFunction :: Writer [String] a
?
How do I call writerStateFunction :: WriterT [String] (State Viable) NUM
(the difference between gameRoutine
definition is ReaderT
layer missing)?
Clearly I don't want to lift their definitions to one of gameRoutine
.
Upvotes: 3
Views: 97
Reputation: 78011
You can lift a Writer w a
into a MonadWriter
instance by, something like:
import Control.Monad.Writer (MonadWriter, Writer, runWriter, tell)
lift' :: MonadWriter w m => Writer w a -> m a
lift' wr = do
let (a, w) = runWriter wr
tell w -- manually re-log the log msg
return a -- wrap value into new context
then, you may simply write:
gameRoutine :: WriterT [String] (ReaderT Env (State Viable)) NUM
gameRoutine = do
a <- lift' writerFunction
as long as writerFunction :: Writer [String] a
can be specialized with a ~ NUM
.
Upvotes: 3
Reputation: 120741
Well, basically the idea of mtl
is that you don't need to do that. Instead of defining writerStateFunction
with that specific signature, you define it with a generic signature
writerStateFunction' :: (MonadWriter [String] m, MonadState Viable m)
=> m NUM
Then it doesn't matter that the environment where you try to use this has an extra Reader
layer: this doesn't prevent the monad from having both MonadWriter
and MonadState
functionality.
Upvotes: 5