mtl,读者,除了&堆叠顺序

时间:2017-07-07 19:25:17

标签: haskell monad-transformers

所以这会有点长,因为我不确定如何更普遍地构建这个问题。好消息是我在问题的底部有一个代码示例,这个想法只是为了使它构建和放置。优雅: - )

我有几个具有签名的功能:

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

1 个答案:

答案 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