2010-10-31 7 views
7

Ho bisogno di scrivere una monade di stato che supporti anche la gestione degli errori. Stavo pensando di usare l'Either Monad per questo scopo perché può anche fornire dettagli su cosa ha causato l'errore. Ho trovato una definizione per una monade di stato usando la monad Forse, ma non sono in grado di modificarla per usare E, invece di Forse. Ecco il codice:Come posso scrivere una monade di stato che gestisce anche la gestione degli errori?

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} 

risposta

6

Esistono due soluzioni possibili. Quello che è più vicino al codice che hai fornito sopra è:

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 
    } 

L'altra forma sposta il trattamento entro la movimentazione stato di errore:

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

Non vedo la differenza tra il primo blocco di codice e il secondo. Hai erroneamente incluso lo stesso codice due volte o, in caso contrario, puoi chiarire la differenza? – seh

+0

@seh, buona cattura, è aggiornato –

+3

Si noti inoltre che questi due sono operativamente un po 'diversi. La seconda versione consente di ripristinare gli errori, mentre la prima versione termina con il primo errore. Se si sta eseguendo la modellazione della registrazione, tenere presente che la prima versione "perde" anche l'errore di accesso. –

4

Hai bisogno di un trasformatore di monade. Le librerie di trasformatori Monad come mtl consentono di comporre diverse monadi per creare una nuova versione. Utilizzando mtl, è possibile definire

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

che vi permetterà di accedere sia stato e la gestione all'interno della vostra StateMonad errore.

2

È sempre possibile utilizzare un trasformatore monad ErrorT con una monade di stato all'interno (o viceversa). Dai un'occhiata alla sezione dei trasformatori di all about monads.

HTH,

+1

Link aggiornato: http://www.haskell.org/haskellwiki/All_About_Monads – sinelaw

9

Considerare l'utilizzo ExceptT da Control.Monad.Trans.Except (invece di utilizzare entrambi i casi).

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 

Se non si ha familiarità con Monadi e trasformatori Monade poi farei che prima! Sono un enorme aiuto e le prestazioni di produttività del programmatore vincono.