2015-02-08 110 views
3

我使用ReaderT Monad转换器通过执行IO的几个函数从我的主函数传播配置数据。将需要数据的最终功能不会执行任何IO。我有这个工作液:如何让Reader和ReaderT一起工作

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 

EDIT 2

我得到了它,我只需要使用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 
+3

是的。让编译器推断每个函数的最一般类型。如果你这样做,'funIO''编译。或者甚至是'funNoIO :: Monad m => ReaderT Configuration m String'都可以工作 - 因为你从不操纵内部monad,所以它可以是任何东西。那么'funIO''的一个合理类型是'MonadIO m => ReaderT Configuration m String' - MonadIO约束将来自定义内部对'liftIO'的任何使用。 – user2407038 2015-02-08 22:56:50

+1

你可以保持funNoIO为简单的Reader,如果你将它与'IO'相关的东西混合使用'mmorph'方法https://hackage.haskell.org/package/mmorph-1.0.4,写'提升概括funNoIO'。文档中的示例使用与您的状态完全平行的“State/StateT”示例。 “提升机”有许多其他应用。 – Michael 2015-02-09 01:14:45

回答

2

你可以将funNoIOfunIO的类型更改为通过monad类型si进行参数化NCE他们不习惯:

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

修复编译器错误,那么你可以改变main到:

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

你也可以让他们的类型签名'MonadReader Configuration m => m String' – Mokosha 2015-02-09 01:30:22

3

另一种方式是与runReader一起使用的MonadReaderreader方法:

funIO = reader $ runReader funNoIO 

reader . runReader将纯粹的Reader monad转换为更属的MonadReader实例。