我需要编写一个也可以支持错误处理的状态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}
答案 0 :(得分:11)
考虑使用ExceptT
中的Control.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变形金刚感到不舒服,那么我先做 !他们是一个巨大的帮助和程序员生产力表现的胜利。
答案 1 :(得分: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
}
答案 2 :(得分:4)
你需要一个monad变压器。 Monad变换器库(例如mtl)允许您组合不同的monad以创建新版本。使用mtl,您可以定义
type StateMonad e a = StateT State (Either e) a
允许您访问StateMonad
中的状态和错误处理。
答案 3 :(得分:4)
我在这里没有看到有人提到论文Monad Transformers Step by Step by Martin Grabmüller
我发现在学习组合monads方面非常有帮助。
答案 4 :(得分:2)
您可以随时使用带有状态monad的ErrorT monad转换器(反之亦然)。 看看all about monads的变形金刚部分。
HTH,
答案 5 :(得分: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)a 堆栈:
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")