Question

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!

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top