2010-10-31 75 views
7

我需要编写一个状态monad,它也可以支持错误处理。我正在考虑使用Either monad来达到这个目的,因为它也可以提供有关导致错误的详细信息。我找到了使用Maybe monad的状态monad的定义,但是我无法修改它来使用Either而不是Maybe。以下是代码:如何编写一个执行错误处理的状态monad?

newtype StateMonad a = StateMonad (State -> Maybe (a, State)) 

instance Monad StateMonad where 
(StateMonad p) >>= k = StateMonad (\s0 -> case p s0 of 
           Just (val, s1) -> let (StateMonad q) = k val in q s1 
           Nothing -> Nothing) 
return a = StateMonad (\s -> Just (a,s)) 

data State = State 
{ log :: String 
, a :: Int} 

回答

6

有两种可能的解决方案。在一个最接近你上面提供的代码是:

newtype StateMonad e a = StateMonad (State -> Either e (a, State)) 

instance Monad (StateMonad e) where 
    (StateMonad p) >>= k = 
     StateMonad $ \s0 -> 
      case p s0 of 
       Right (val, s1) -> 
        let (StateMonad q) = k val 
        in q s1 
       Left e -> Left e 
    return a = StateMonad $ \s -> Right (a, s) 

data State = State 
    { log :: String 
    , a :: Int 
    } 

的其他形式的移动误差时的状态处理中操作:

newtype StateMonad e a = StateMonad (State -> (Either e a, State)) 

instance Monad (StateMonad e) where 
    (StateMonad p) >>= k = 
     StateMonad $ \s0 -> 
      case p s0 of 
       (Right val, s1) -> 
        let (StateMonad q) = k val 
        in q s1 
       (Left e, s1) -> (Left e, s1) 
    return a = StateMonad $ \s -> (Right a, s) 

data State = State 
    { log :: String 
    , a :: Int 
    } 
+0

我没有看到第一个代码块和第二个代码块之间的区别。你是否错误地包含了相同的代码两次,或者,如果不是,你能澄清它们之间的区别吗? – seh 2010-10-31 15:57:34

+0

@seh,良好的捕获,它的更新 – 2010-10-31 16:07:42

+3

还要注意,这两个在操作上有点不同。 第二个版本允许可恢复的错误,而第一个版本终止于第一个错误。如果您正在建模日志记录,请注意第一个版本也会“失去”登录错误。 – 2010-10-31 16:26:11

4

你需要一个单子转换。 Monad转换器库(如mtl)允许您编写不同的单声道以创建新版本。使用MTL,你可以定义

type StateMonad e a = StateT State (Either e) a 

,这将允许您访问州和错误您StateMonad内处理。

2

您可以随时在内部使用带有状态monad的ErrorT monad变换器(反之亦然)。 看看all about monads的变形金刚部分。

HTH,

+1

更新后的链接:http://www.haskell.org/haskellwiki/All_About_Monads – sinelaw 2014-09-16 19:06:29

9

考虑使用从Control.Monad.Trans.ExceptExceptT(而不是使用任一)。

import Control.Monad.State 
import Control.Monad.Trans.Except 
import Control.Monad.Identity 

data MyState = S 

type MyMonadT e m a = StateT MyState (ExceptT e m) a 

runMyMonadT :: (Monad m) => MyMonadT e m a -> MyState -> m (Either e a) 
runMyMonadT m = runExceptT . evalStateT m 

type MyMonad e a = MyMonadT e Identity a 
runMyMonad m = runIdentity . runMyMonadT m 

如果你不舒服的单子和单子变压器然后我会做到这一点第一!他们是一个巨大的帮助和程序员生产力的表现胜利。

相关问题