【问题标题】:What's the difference between StateT s (ExceptT e m) and ExceptT e (StateT s m)?StateT s (ExceptT e m) 和 ExpectT e (StateT s m) 有什么区别?
【发布时间】:2015-08-06 01:29:27
【问题描述】:

Monad 转换器很棘手,我不确定(= 没有良好的直觉)哪个应该放在首位。

【问题讨论】:

  • @luqui 没找到。但是我想保留这个问题:至少它的范围要窄得多,因此可以更好地回答。
  • 顺便说一下,lambdabot 有一个 monad-stack-unroller。在#haskell 频道中尝试?unmtl

标签: haskell monad-transformers


【解决方案1】:
StateT s (ExceptT e m)

这说:

  • m开头
  • 为此添加例外
  • 为其添加状态

现在,“添加异常”意味着您的操作可以通过两种方式终止:正常返回值或异常。

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

所以在StateT s (ExceptT e m)中,只有在没有异常的情况下才会得到一个结果状态。

另一方面,

ExceptT e (StateT s m)

说:

  • m开头
  • 为其添加状态
  • 为此添加例外

“添加状态”表示在m 的返回值中包含额外的状态输出。

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

【讨论】:

  • 这可能会给出更清晰的画面,因此:StateT s (ExceptT e m) a 具有“扩展”形式:s -> m (Either e (s, a))ExceptT e (StateT s m)“扩展”为:s -> m (s, Either e a)。前者允许您在catchError 中发生错误时回滚对状态的更改,而后者则提交错误状态,使它们更难回滚。在某些语言中,您实际上想要后者。推荐阅读:cse.chalmers.se/edu/course/…
【解决方案2】:

我自己回答这个问题,但欢迎其他答案!

考虑这个例子:

#!/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

【讨论】:

    猜你喜欢
    • 2013-09-11
    • 1970-01-01
    • 2015-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 2010-12-14
    相关资源
    最近更新 更多