所以这会有点长,因为我不确定如何更普遍地构建这个问题。好消息是我在问题的底部有一个代码示例,这个想法只是为了使它构建和放置。优雅: - )
我有几个具有签名的功能:
calledFunction
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult)
calledFunction2
:: (MonadReader env m, HasToken env, MonadIO m)
=> m (Either MyError MyResult2)
我想最后得到ExceptT String IO MyResult3
类型的结果,我将MyResult
& MyResult2
。
现在calledFunction
返回Either
非常好,因为我可以利用:
ExceptT :: m (Either e a) -> ExceptT e m a
我只需输入EitherT calledFunction
,我就不再拥有m (Either MyError MyResult)
,而是直接ExceptT MyError m MyResult)
。进步!
但我还需要向calledFunction
提供所需的读者语境。现在,我会用runReaderT
做到这一点。我现在来到ExceptT MyError m MyResult
变换器堆栈,所以ReaderT
自然应该到m
所在的位置。所以ExceptT MyError (ReaderT Config IO) MyResult
...
除此之外,我如何填写' readerT具有要读取的值,因为它位于变压器堆栈的底部?如果我反转堆栈以使读者处于顶层,那么runReaderT
自然会出现,但我不知道如何使用EitherT
来转换Either
中的ExceptT
{1}}优雅地......
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
答案 0 :(得分:4)
您可以使用hoist
中的Control.Monad.Morph
来运行Reader
下面的ExceptT
:
ghci> let foo = undefined :: ExceptT () (ReaderT () IO) ()
ghci> :t hoist (flip runReaderT ()) foo
hoist (flip runReaderT ()) foo :: ExceptT () IO ()
自己动手也很容易,只需要用runExceptT
打开,用runReader
提供环境,然后在ExceptT
构造函数中重新包装结果:
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