我怎样才能编写一个执行错误处理的状态monad?

问题描述 投票:8回答:6

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

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}
haskell state monads monad-transformers state-monad
6个回答
11
投票

考虑使用来自ExceptTControl.Monad.Trans.Except(而不是使用Either)。

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

如果你对Monads和Monad变形金刚感到不舒服,那么我会先做到这一点!他们是一个巨大的帮助和程序员生产力表现的胜利。


7
投票

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

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
    }

4
投票

你需要一个monad变压器。 Monad变换器库(如mtl)允许您组合不同的monad来制作新版本。使用mtl,您可以定义

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

这将允许您访问StateMonad中的状态和错误处理。


4
投票

我没有看到有人提到纸张Monad Transformers Step by Step by Martin Grabmüller

我发现它在学习组合monad方面非常有帮助。


2
投票

您始终可以使用ErrorT monad转换器,其中包含State monad(反之亦然)。看看all about monads的变形金刚部分。

HTH,


0
投票

刚看到像这样的例子

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

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

但据我了解,如果出现错误,您将失去状态,因为在此处您可以在Either / Except中添加状态,因此只能在Right中访问状态。如果你需要处理错误和获取状态,这是计算到发生错误的时刻,你可以使用ExceptT e(State s)堆栈:

type StateExcept e s a = ExceptT e (State s) a

test :: Int -> StateExcept String String ()
test limit =  do
    modify (succ . head >>= (:)) -- takes first char from state and adds next one in alphabet to state
    s <- get
    when (length s == limit) (throwError $ "State reached limit of " ++ show limit)

runTest :: ExceptT String (State String) () -> (Either String (), [Char])
runTest se = runState (runExceptT se) "a"


λ: runTest (forever $ test 4)
(Left "State reached limit of 4","dcba")
λ: runTest (replicateM_ 2 $ test 4)
(Right (),"cba")
© www.soinside.com 2019 - 2024. All rights reserved.