2010-10-31 48 views
7

我需要編寫一個狀態monad,它也可以支持錯誤處理。我正在考慮使用Either monad來達到這個目的,因爲它也可以提供有關導致錯誤的詳細信息。我找到了使用Maybe monad的狀態monad的定義,但是我無法修改它來使用Either而不是Maybe。以下是代碼:如何編寫一個執行錯誤處理的狀態monad?

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} 

回答

6

有兩種可能的解決方案。在一個最接近你上面提供的代碼是:

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 
    } 
+0

我沒有看到第一個代碼塊和第二個代碼塊之間的區別。你是否錯誤地包含了相同的代碼兩次,或者,如果不是,你能澄清它們之間的區別嗎? – seh 2010-10-31 15:57:34

+0

@seh,良好的捕獲,它的更新 – 2010-10-31 16:07:42

+3

還要注意,這兩個在操作上有點不同。 第二個版本允許可恢復的錯誤,而第一個版本終止於第一個錯誤。如果您正在建模日誌記錄,請注意第一個版本也會「失去」登錄錯誤。 – 2010-10-31 16:26:11

4

你需要一個單子轉換。 Monad轉換器庫(如mtl)允許您編寫不同的單聲道以創建新版本。使用MTL,你可以定義

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

,這將允許您訪問州和錯誤您StateMonad內處理。

2

您可以隨時在內部使用帶有狀態monad的ErrorT monad變換器(反之亦然)。 看看all about monads的變形金剛部分。

HTH,

+1

更新後的鏈接:http://www.haskell.org/haskellwiki/All_About_Monads – sinelaw 2014-09-16 19:06:29

9

考慮使用從Control.Monad.Trans.ExceptExceptT(而不是使用任一)。

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 

如果你不舒服的單子和單子變壓器然後我會做到這一點第一!他們是一個巨大的幫助和程序員生產力的表現勝利。