synapski
synapski

Reputation: 333

How to get Reader and ReaderT to work together

I'm using the ReaderT Monad transformer to propagate my configuration data from my main function through several functions performing IO. The ultimate function that will need the data doesn't perform any IO. I have this working solution :

import Control.Monad.Reader

type Configuration = String

funNoIO :: Reader Configuration String
funNoIO = do
    config <- ask
    return $ config ++ "!"

funIO :: ReaderT Configuration IO String
funIO = do
    config <- ask
    return $ runReader funNoIO config

main :: IO ()
main = do
    c <- runReaderT funIO "configuration"
    print c

But it forces me to retrieve the configuration in the funIO function where I don't need it.

I modified it like this :

funIO' :: ReaderT Configuration IO String
funIO' = do
    v <- funNoIO
    return v

but it doesn't compile and I'm getting this error :

Couldn't match type ‘ReaderT Configuration Identity String’
              with ‘Identity (ReaderT Configuration IO String)’
Expected type: Identity (ReaderT Configuration IO String)
  Actual type: Reader Configuration String
In the first argument of ‘runIdentity’, namely ‘funNoIO’
In a stmt of a 'do' block: v <- runIdentity funNoIO

Is it possible to propagate my configuration data to a pure function without retrieving it in the intermediary IO functions?

EDIT

I parameterised my functions but I still can't perform an IO action in the funIO' function. For example :

getMessage :: IO String
getMessage = do
    return "message"

funIO' :: MonadIO m => ReaderT Configuration m String
funIO' = do
    m <- getMessage
    v <- funNoIO
    return $ v ++ m

is giving me the following error :

Couldn't match type ‘IO’ with ‘ReaderT Configuration m’
Expected type: ReaderT Configuration m String
Actual type: IO String

EDIT 2

I got it, I just needed to use liftIO :

getMessage :: IO String
getMessage = do
    return "message"

funIO' :: MonadIO m => ReaderT Configuration m String
funIO' = do
    m <- liftIO getMessage
    v <- funNoIO
    return $ v ++ m

Upvotes: 3

Views: 546

Answers (2)

&#216;rjan Johansen
&#216;rjan Johansen

Reputation: 18189

Another way is to use the reader method of MonadReader together with runReader:

funIO = reader $ runReader funNoIO

reader . runReader converts from the pure Reader monad to a more general MonadReader instance.

Upvotes: 3

Lee
Lee

Reputation: 144136

You can change the type of funNoIO and funIO to be parameterised over the monad type since they aren't used:

funNoIO :: Monad m => ReaderT Configuration m String
funIO' :: Monad m => ReaderT Configuration m String

to fix the compiler error, then you can change main to:

main = do
    c <- runReaderT funIO' "configuration"
    print c

Upvotes: 2

Related Questions