2010-10-31 14 views
7

Necesito escribir una mónada de estado que también pueda soportar el manejo de errores. Estaba pensando en utilizar la Mónada de cualquiera para este propósito porque también puede proporcionar detalles sobre qué causó el error. Encontré una definición para una mónada de estado usando la mónada Maybe, sin embargo, no puedo modificarla para usar Either, en vez de Maybe. Aquí está el código:¿Cómo puedo escribir una mónada de estado que también maneje errores?

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} 

Respuesta

6

Existen dos posibles soluciones. El que está más cerca del código que ya ha proporcionado anteriormente es:

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 
    } 

La otra forma se mueve la manipulación en el manejo de estado de error:

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

No veo la diferencia entre el primer bloque de código y el segundo. ¿Incluyó equivocadamente el mismo código dos veces o, de no ser así, puede aclarar la diferencia? – seh

+0

@seh, buena captura, está actualizado –

+3

También tenga en cuenta que estos dos son operacionalmente un poco diferentes. La segunda versión permite errores reanudables, mientras que la primera versión termina en el primer error. Si está modelando el registro, tenga en cuenta que la primera versión también 'pierde' el error de inicio de sesión. –

4

Se necesita un transformador de mónada. Las bibliotecas de transformadores de Monad como mtl le permiten componer diferentes mónadas para crear una nueva versión. El uso de MTL, se podría definir

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

que le permitirá acceder tanto el Estado como el manejo de errores dentro de su StateMonad.

2

Siempre puede usar un transformador de mónada ErrorT con una mónada de estado adentro (o viceversa). Eche un vistazo a la sección de transformadores de all about monads.

HTH,

+1

Enlace actualizado: http://www.haskell.org/haskellwiki/All_About_Monads – sinelaw

9

Considere el uso de ExceptT de Control.Monad.Trans.Except (en lugar de utilizar tampoco).

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 

Si no se siente cómodo con Mónadas y transformadores Monad entonces yo haría eso primero! Son una gran ayuda y la productividad del programador gana.

Cuestiones relacionadas