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
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.
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
Related
So this is going to be a bit long, because I'm not sure how to frame this question more generally. The good news is that I have a code sample at the bottom of the question, the idea is just to make it build & elegant :-)
I have a couple of functions which have signatures like:
calledFunction
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult)
calledFunction2
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult2)
And I'd like to get in the end a result of type ExceptT String IO MyResult3 which I get by combining MyResult & MyResult2.
Now it's very nice that calledFunction returns an Either because I can leverage:
ExceptT :: m (Either e a) -> ExceptT e m a
And I just type EitherT calledFunction and I won't have anymore m (Either MyError MyResult) but straight ExceptT MyError m MyResult). Progress!
But I also need to give to calledFunction the reader context it wants. Now, I would do that with runReaderT. I have now come to the ExceptT MyError m MyResult transformer stack, so naturally the ReaderT should go where the m is.. So ExceptT MyError (ReaderT Config IO) MyResult...
Except, how do I 'fill in' the readerT with the value to read, since it's at the bottom of the transformer stack? And if I reverse the stack to have the reader at the toplevel, then runReaderT comes naturally, but I don't see how to use EitherT to transform my Either in an ExceptT elegantly...
import Control.Monad.Reader
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Except
import Control.Monad.IO.Class
import Control.Error -- 'error' package
class HasToken a where
getToken :: a -> String
data Config = Config String
instance HasToken Config where
getToken (Config x) = x
data MyError = MyError String deriving Show
data MyResult = MyResult String
data MyResult2 = MyResult2 String
data MyResult3 = MyResult3 MyResult MyResult2
calledFunction
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult)
calledFunction = undefined
calledFunction2
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult2)
calledFunction2 = undefined
cfg = Config "test"
main = undefined
test :: ExceptT MyError IO MyResult3
test = do
-- calling runReaderT each time defeats the purpose..
r1 <- ExceptT (runReaderT calledFunction cfg)
r2 <- ExceptT (runReaderT calledFunction2 cfg)
return $ MyResult3 r1 r2
test1 = runReaderT test2 cfg
test2 :: ReaderT Config (ExceptT MyError IO) MyResult3
test2 = do
-- how to make this compile?
let cfg = Config "test"
r1 <- ExceptT calledFunction
r2 <- ExceptT calledFunction2
return $ MyResult3 r1 r2
You can use hoist from Control.Monad.Morph to run the Reader below the ExceptT:
ghci> let foo = undefined :: ExceptT () (ReaderT () IO) ()
ghci> :t hoist (flip runReaderT ()) foo
hoist (flip runReaderT ()) foo :: ExceptT () IO ()
It's also easy to do it yourself, you just have to unwrap with runExceptT, supply the environment with runReader, and re-wrap the result in the ExceptT constructor:
ghci> :t \env -> ExceptT . flip runReaderT env . runExceptT
\env -> ExceptT . flip runReaderT env . runExceptT
:: r -> ExceptT e (ReaderT r m) a -> ExceptT e m a
type C = StateT Int (ErrorT String IO)
main :: C ()
main = do
args <- liftIO $ getArgs
case args of
[] -> liftIO $ putStrLn "E"
(p:ps) -> s "d"
s :: String -> C ()
I've got the error:
Couldn't match type ‘Control.Monad.Trans.State.Lazy.StateT
Int
(Control.Monad.Trans.Error.ErrorT String IO)
()’
with ‘IO t0’
Expected type: IO t0
Actual type: C ()
In the expression: main
When checking the type of the IO action ‘main’
I cannot understand why there is an error and how to repair it. Please help.
main must be of type IO a for some a (usually, but not necessarily, ()). It can't be of type StateT Int (ErrorT String IO). You can fix this by using the functions provided with StateT and ErrorT for converting them to actions of the underlying monad.
I'm using Servant with custom monad stack:
newtype AppHandler a = AppHandler { runHandler :: ReaderT Config (ExceptT ServantErr IO) a }
deriving (Functor, Applicative, Monad, MonadReader Config, MonadError ServantErr, MonadIO)
data Config = Config
{ getPool :: ConnectionPool }
Now, in many handlers I just need to do fetch some data (Persistent) from db and act upon it, so I've got:
runDb :: (MonadReader Config m, MonadIO m) => SqlPersistT IO b -> m b
runDb query = do
pool <- asks getPool
liftIO $ runSqlPool query pool
As it turns out, when fetching from db, you're bound to work with Maybe, and quite often when the Maybe is Nothing, you just want to throw error so that Servant server will turn it into proper HTTP response. This led me to the discovery of Control.Error.Util and the (!?) :: Applicative m => m (Maybe a) -> e -> ExceptT e m a helper. So I tried following:
someHandler :: AppHandler NoContent
someHandler = do
entity <- (runDb $ getCompanyByName companyName) !? err400
-- some more logic
return NoContent
But this does not compile, the result of !? here is ExceptT ServantErr m0 (Entity SomeEntity) but I no longer work with such handler type, it requires the AppHandler (Entity SomeEntity). How would I convert such value back to my handler type?
You want a variant of (!?) that is polymorphic in which monad it returns. For example:
(!??) :: MonadError e m => m (Maybe a) -> e -> m a
act !?? err = act >>= maybe (throwError err) return
Then, provided err400 :: ServantError -- which is the type of errors you declared AppHandler to have -- you will be able to write
runDb (getCompanyByName companyName) !?? err400
In general to convert a value of type m a to ReaderT r m a just use lift.
So perhaps this will work for you:
entity <- lift $ (runDb $ getCompanyByName companyName) !? err400
if the entire (runDb ...) !? err400 is an ExceptT ServantError ... value.
Also, this servant issue discussion:
https://github.com/haskell-servant/servant/issues/286
might be helpful.
This is a contrieved example using Reader Transformer:
{-# LANGUAGE UnicodeSyntax #-}
import Control.Monad.Reader
import Data.Char
conv ∷ Int → ReaderT Char Maybe String
conv n = do
yn ← ask
if yn == 'y'
then return $ chr n : " with Yes"
else lift Nothing
-- runReaderT (conv 98) 'y' Just "b with Yes"
-- runReaderT (conv 98) '#' Nothing
inspect ∷ ReaderT Char Maybe String → Bool
-- EDITED: as per suggestions, the correct type is monadic:
inspect ∷ ReaderT Char Maybe String → ReaderT Char Maybe Bool
inspect is supposed to check whether the value inside is Nothing. Can it be done, or I am having "design issues"?
ReaderT is exactly
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
-- a function that reads the environment^ |
-- and returns an m a^
For ReaderT r Maybe a it's a function that reads the environment and returns a Maybe a. You can make a function that reads the environment and checks if the result is Nothing by composing this function with another function that checks whether the result is Nothing. To check whether a Maybe a is nothing, we can use isJust, and to pack the resulting Bool back into a Maybe Bool we'd use return.
inspect :: ReaderT r Maybe a -> ReaderT r Maybe Bool
inspect (ReaderT f) = ReaderT $ return . isJust . f
transformers provides a function, mapReaderT that lets us manipulate the computation inside a ReaderT
mapReaderT :: (m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT f m = ReaderT $ f . runReaderT m
mapReaderT just composes the function provided as its first argument with the function inside the ReaderT (runReaderT unwraps the function inside a ReaderT). You can use mapReaderT to write inspect more elegantly.
inspect :: ReaderT r Maybe a -> ReaderT r Maybe Bool
inspect = mapReaderT (return . isJust)
How do I take values from an IO monad and interpolate into a yesod widget?
For example, I want to interpolate the contents of a file into hamlet:
(readFile "test.txt") >>= \x -> toWidget $ [hamlet| <p> text: #{x} |]
or equivalently:
contents <- (readFile "test.txt")
toWidget $ [hamlet| <h2> foo #{contents} |]
There's something basic I'm not grasping about how interpolation interacts with IO since neither of these type checks:
Couldn't match type ‘IO’ with ‘WidgetT App IO’ …
Expected type: WidgetT App IO String
Actual type: IO String
These errors occur in a getHomeR route function.
If I try to do something similar in GHCi with a predefined function, I get a different error. In the source file, I have a function:
makeContent body =
[hamlet|
<h2> foo
<div> #{body}
|]
in GHCi:
(readFile "test.txt") >>= \x -> makeContent x
And I get an error due to insufficient arguments (I think this is because of some template magic I don't understand yet):
<interactive>:139:33:
Couldn't match expected type ‘IO b’
with actual type ‘t0 -> Text.Blaze.Internal.MarkupM ()’
Relevant bindings include it :: IO b (bound at <interactive>:139:1)
Probable cause: ‘makeContent’ is applied to too few arguments
When working with monad transformers, to convert from some monad m to a transformer t m, you have to use the lift function:
lift :: (MonadTrans t, Monad m) => m a -> t m a
This is actually the defining method of the MonadTrans typeclass, so the implementation is specific to the transformer t.
If you want to perform IO actions inside a transformer, you'll have to define an instance for MonadIO, which has the liftIO method:
liftIO :: (MonadIO m) => IO a -> m a
an instance of MonadIO doesn't have to be a transformer, though, IO is an instance where liftIO = id. These two functions are designed to let you "pull" actions up a transformer stack, for each level in the stack you would call lift or liftIO once.
For your case, you have the stack WidgetT App IO, with the transformer WidgetT App and the base monad IO, so you only need one call to liftIO to pull an IO action up to be an action in the WidgetT App IO monad. So you would just do
liftIO (readFile "test.txt") >>= \x -> makeContent x
A lot of developers (including myself) find liftIO a bit of a burden to type when you have a lot of IO actions, so it's not uncommon to see something like
io :: MonadIO io => IO a -> io a
io = liftIO
putStrLnIO :: MonadIO io => String -> io ()
putStrLnIO = io . putStrLn
printIO :: (MonadIO io, Show a) => a -> io ()
printIO = io . print
readFileIO :: MonadIO io => FilePath -> io String
readFileIO = io . readFile
And so on. If you find yourself using a lot of liftIOs in your code, this can help trim down on how many characters you need to type.