I have the following boilerplate that I do quite often, and would like to eliminate. It looks something like this:
type Configured = ReaderT Config
doSomething :: Configured IO Data
doSomething = do
getMeta <- asks getMetaData
meta <- liftIO getMeta
I'd like to reduce that to something like this:
doSomething = do
meta <- find getMetaData
Unfortunately, I haven't fully wrapped my mind around monad transformers yet. What is the type of find
? Is it (Config -> IO Result) -> Result
? How do I write it?
Any tips / explanation to help me grok monad transformers are very much appreciated.
Thanks!
This can be done in a fairly mechanical fashion. Let's start with your original code:
doSomething = do
getMeta <- asks getMetaData
meta <- liftIO getMeta
...
Using the third monad law, we are allowed to move the part we want to extract into a do-block of its own:
doSomething = do
meta <- do getMeta <- asks getMetaData
liftIO getMeta
...
Next, we can just extract that subexpression and give it a name:
findMetaData = do getMeta <- asks getMetaData
liftIO getMeta
doSomething = do
meta <- findMetaData
...
Finally, let's generalize it by replacing the explicit reference to getMetaData
with a parameter:
find something = do x <- asks something
liftIO x
doSomething = do
meta <- find getMetaData
...
Now, we can load it in GHCi and ask it to infer the type for us:
*Main> :t find
find :: (MonadReader r m, MonadIO m) => (r -> IO b) -> m b
Optionally, we might want to clean it up a little and remove the dummy name x
:
find something = ask >>= liftIO . something
To do this, I used the definition of asks
and the desugaring rules for do-notation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With