StateT s (ExceptT e m) 和 ExceptT e (StateT s m) 有什么区别?
What's the difference between StateT s (ExceptT e m) and ExceptT e (StateT s m)?
Monad 转换器很棘手,我不确定(= 没有很好的直觉)哪个应该放在最上面。
我自己回答这个问题,但欢迎其他答案!
考虑示例:
#!/usr/bin/env stack
-- stack runghc --package mtl
{-# LANGUAGE FlexibleContexts #-}
module Main (main) where
import Control.Applicative
import Control.Monad.State
import Control.Monad.Error
import Control.Monad.Trans.Except
import Data.Functor.Identity
test1 :: (MonadState Int m, MonadError String m) => m Bool
test1 = do
put 1
throwError "foobar"
put 2
return False
test2 :: (Alternative m, MonadState Int m, MonadError String m) => m Bool
test2 = do
put 4
test1 <|> return True
runStateExceptT :: Monad m => s -> ExceptT e (StateT s m) a -> m (Either e a, s)
runStateExceptT s = flip runStateT s . runExceptT
runExceptStateT :: Monad m => s -> StateT s (ExceptT e m) a -> m (Either e (a, s))
runExceptStateT s = runExceptT . flip runStateT s
main :: IO ()
main = do
print $ runIdentity . runStateExceptT 3 $ test1
print $ runIdentity . runExceptStateT 3 $ test1
print $ runIdentity . runStateExceptT 3 $ test2
print $ runIdentity . runExceptStateT 3 $ test2
它将打印:
(Left "foobar",1)
Left "foobar"
(Right True,1)
Right (True,4)
在 ExceptT
外面,您仍然会得到 "throwing an error" 时刻的状态。这可能就是你想要的。
请记住,这种组合很像命令式编程。应该考虑异常安全实践,即必须注意何时 throwError
!
StateT s (ExceptT e m)
这表示:
- 从
m
开始
- 添加例外情况
- 向其添加状态
现在,'adding exceptions' 意味着您的操作可以通过两种方式终止:以正常 return 值终止,或以异常终止。
'Adding state' 意味着 正常 return 值 .
中包含额外的状态输出位
所以在StateT s (ExceptT e m)
中,如果没有异常,你只会得到一个结果状态。
另一方面,
ExceptT e (StateT s m)
说:
- 从
m
开始
- 向其添加状态
- 添加例外情况
'Adding state' 表示 m
.
的 return 值中包含额外的状态输出位
但是现在,您添加的异常会作为替代 return 值 添加到 StateT
monad 中。所以你总是得到一个状态输出,然后你可能会得到一个正常的 return 值或者你可能会得到一个异常。
Monad 转换器很棘手,我不确定(= 没有很好的直觉)哪个应该放在最上面。
我自己回答这个问题,但欢迎其他答案!
考虑示例:
#!/usr/bin/env stack
-- stack runghc --package mtl
{-# LANGUAGE FlexibleContexts #-}
module Main (main) where
import Control.Applicative
import Control.Monad.State
import Control.Monad.Error
import Control.Monad.Trans.Except
import Data.Functor.Identity
test1 :: (MonadState Int m, MonadError String m) => m Bool
test1 = do
put 1
throwError "foobar"
put 2
return False
test2 :: (Alternative m, MonadState Int m, MonadError String m) => m Bool
test2 = do
put 4
test1 <|> return True
runStateExceptT :: Monad m => s -> ExceptT e (StateT s m) a -> m (Either e a, s)
runStateExceptT s = flip runStateT s . runExceptT
runExceptStateT :: Monad m => s -> StateT s (ExceptT e m) a -> m (Either e (a, s))
runExceptStateT s = runExceptT . flip runStateT s
main :: IO ()
main = do
print $ runIdentity . runStateExceptT 3 $ test1
print $ runIdentity . runExceptStateT 3 $ test1
print $ runIdentity . runStateExceptT 3 $ test2
print $ runIdentity . runExceptStateT 3 $ test2
它将打印:
(Left "foobar",1)
Left "foobar"
(Right True,1)
Right (True,4)
在 ExceptT
外面,您仍然会得到 "throwing an error" 时刻的状态。这可能就是你想要的。
请记住,这种组合很像命令式编程。应该考虑异常安全实践,即必须注意何时 throwError
!
StateT s (ExceptT e m)
这表示:
- 从
m
开始
- 添加例外情况
- 向其添加状态
现在,'adding exceptions' 意味着您的操作可以通过两种方式终止:以正常 return 值终止,或以异常终止。
'Adding state' 意味着 正常 return 值 .
中包含额外的状态输出位所以在StateT s (ExceptT e m)
中,如果没有异常,你只会得到一个结果状态。
另一方面,
ExceptT e (StateT s m)
说:
- 从
m
开始
- 向其添加状态
- 添加例外情况
'Adding state' 表示 m
.
但是现在,您添加的异常会作为替代 return 值 添加到 StateT
monad 中。所以你总是得到一个状态输出,然后你可能会得到一个正常的 return 值或者你可能会得到一个异常。