mtl,reader,异常和堆叠顺序
mtl, reader, exceptt & stacking order
所以这会有点长,因为我不确定如何更笼统地提出这个问题。好消息是我在问题的底部有一个代码示例,这个想法只是让它构建和优雅:-)
我有几个函数的签名如下:
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
returns 和 Either
非常好,因为我可以利用:
ExceptT :: m (Either e a) -> ExceptT e m a
我只是输入 EitherT calledFunction
,我不会再输入 m (Either MyError MyResult)
,而是直接输入 ExceptT MyError m MyResult)
。进步了!
但我还需要为 calledFunction
提供它想要的 reader 上下文。现在,我会用 runReaderT
来做到这一点。我现在已经来到 ExceptT MyError m MyResult
变压器堆栈,所以 ReaderT
自然应该去 m
所在的位置。所以 ExceptT MyError (ReaderT Config IO) MyResult
...
除了,我如何 'fill in' 具有要读取的值的 readerT,因为它位于转换器堆栈的底部?如果我反转堆栈使 reader 位于顶层,那么 runReaderT
自然会出现,但我不知道如何使用 EitherT
将我的 Either
转换为ExceptT
优雅...
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
您可以使用 hoist
from Control.Monad.Morph
到 运行 ExceptT
下面的 Reader
:
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
所以这会有点长,因为我不确定如何更笼统地提出这个问题。好消息是我在问题的底部有一个代码示例,这个想法只是让它构建和优雅:-)
我有几个函数的签名如下:
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
returns 和 Either
非常好,因为我可以利用:
ExceptT :: m (Either e a) -> ExceptT e m a
我只是输入 EitherT calledFunction
,我不会再输入 m (Either MyError MyResult)
,而是直接输入 ExceptT MyError m MyResult)
。进步了!
但我还需要为 calledFunction
提供它想要的 reader 上下文。现在,我会用 runReaderT
来做到这一点。我现在已经来到 ExceptT MyError m MyResult
变压器堆栈,所以 ReaderT
自然应该去 m
所在的位置。所以 ExceptT MyError (ReaderT Config IO) MyResult
...
除了,我如何 'fill in' 具有要读取的值的 readerT,因为它位于转换器堆栈的底部?如果我反转堆栈使 reader 位于顶层,那么 runReaderT
自然会出现,但我不知道如何使用 EitherT
将我的 Either
转换为ExceptT
优雅...
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
您可以使用 hoist
from Control.Monad.Morph
到 运行 ExceptT
下面的 Reader
:
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