开发者

How can I write a state monad that does error handling as well?

I need to write a state monad that can also support error handling. I was thinking of using the Either monad for this purpose because it can also provide details about what caused the error. I found a definition for a state monad using the Maybe monad however I am unable to modify it to use Either, instead of Maybe. Here's the code:

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 = Stat开发者_如何学编程eMonad (\s -> Just (a,s))

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


Consider using ExceptT from Control.Monad.Trans.Except (instead of using 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

If you aren't comfortable with Monads and Monad transformers then I'd do that first! They are a huge help and programmer productivity performance win.


There are two possible solutions. The one that is closest to the code you provided above is:

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
    }

The other form moves the error handling within the state handling:

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
    }


You need a monad transformer. Monad transformer libraries such as mtl allow you to compose different monads to make a new version. Using mtl, you could define

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

which will allow you to access both state and error handling within your StateMonad.


I didn't see anyone here mention the paper Monad Transformers Step by Step by Martin Grabmüller

I found it to be very helpful in learning about combining monads.


You can always use a ErrorT monad transformer with a State monad inside (or vice versa). Have a look at the transformers section of all about monads.

HTH,


Just saw examples like

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

and

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

but as far as I understand, you will lose your state in case of error, because here you add state inside Either/Except, so state will be only accessible in Right. If you need handle error and get state, which was computed up to moment where error occurred, you can use ExceptT e (State s) a stack:

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")
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜