我正在使用ReaderT Monad转换器将配置数据从我的主函数通过执行IO的几个函数传播。需要数据的最终函数不执行任何IO操作。我有一个可行的解决方案:
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
但这迫使我在funIO
函数中检索配置,而我并不需要它。
我将其修改如下:
funIO' :: ReaderT Configuration IO String
funIO' = do
v <- funNoIO
return v
但是它无法编译,我得到了这个错误信息:
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
是否有可能在不在中间IO函数中检索配置数据的情况下将其传播到纯函数中?
编辑
我给我的函数参数化了,但我仍然无法在funIO'
函数中执行IO操作。例如:
getMessage :: IO String
getMessage = do
return "message"
funIO' :: MonadIO m => ReaderT Configuration m String
funIO' = do
m <- getMessage
v <- funNoIO
return $ v ++ m
给我报了以下错误:
Couldn't match type ‘IO’ with ‘ReaderT Configuration m’
Expected type: ReaderT Configuration m String
Actual type: IO String
编辑二
我明白了,我只需要使用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
funIO'
就可以编译了。甚至funNoIO :: Monad m => ReaderT Configuration m String
也可以工作——因为您从未操纵内部的 monad,所以它可以是任何东西都有意义。然后funIO'
的一个合理类型是MonadIO m => ReaderT Configuration m String
——MonadIO 约束将来自于定义中任何对liftIO
的使用。 - user2407038IO
的内容混合时使用mmorph
方法 https://hackage.haskell.org/package/mmorph-1.0.4,并编写`hoist generalize funNoIO,则可以将其保留为普通Reader。文档中的示例使用了一个与您完全相似的
State/StateT示例。
hoist`还有许多其他应用。 - Michael