StateT s(ExceptT e m)和ExceptT e(StateT s m)之间有什么区别?

时间:2015-08-05 17:10:03

标签: haskell monad-transformers

Monad变形金刚很棘手,我不确定(=没有良好的直觉)哪一个应该是最重要的。

2 个答案:

答案 0 :(得分:14)

StateT s (ExceptT e m)

这说:

  • m
  • 开始
  • 添加例外
  • 将状态添加到

现在,'添加例外'表示您的操作可以通过两种方式终止:具有正常返回值或具有异常。

'添加州'意味着额外的状态输出包含在正常返回值中。

因此,在StateT s (ExceptT e m)中,只有在没有异常时才会获得结果状态。

另一方面,

ExceptT e (StateT s m)

表示:

  • m
  • 开始
  • 将状态添加到
  • 添加例外

'添加州'意味着额外的状态输出包含在m的返回值中。

但是现在,您添加的异常会作为StateT monad 中的替代返回值添加。所以你总是得到一个状态输出,然后你可能得到一个正常的返回值,或者你可能会得到一个例外。

答案 1 :(得分:6)

我自己回答,但欢迎其他答案!

考虑一下这个例子:

#!/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之外,您仍然可以获得“抛出错误”的状态。这可能就是你想要的。

请记住,这种组合类似于命令式编程很多。人们应该考虑例外安全实践,即必须注意何时到throwError