2016-06-13 48 views
3

我的代碼結構如下例所示。我很確定應該有一種方法來更安全地構建它。我會假設無論是(或錯誤)monad可以幫助,但我不知道從哪裏開始。任何指針讓我朝着正確的方向前進?如何避免金字塔病例?

data Data1 = Data1 { d2Id :: String } 
data Data2 = Data2 { d3Id :: String } 
data Data3 = Data3 { d4Id :: String } 

getData1 :: String -> IO (Either String Data1) 
getData2 :: String -> IO (Either String Data2) 
getData3 :: String -> IO (Either String Data3) 

process :: Data1 -> Data2 -> Data3 -> IO() 

get :: String -> IO() 
get id = do 
    r1 <- getData1 id 
    case r1 of 
    Left err -> print err 
    Right d1 -> do 
     r2 <- getData2 $ d2Id d1 
     case r2 of 
     Left err -> print err 
     Right d2 -> do 
      r3 <- getData3 $ d3Id d2 
      case r3 of 
      Left err -> print err 
      Right d3 -> do 
       process d1 d2 d3 

回答

3

,因爲我認爲這將是有益的,看看 如何將這種特定的代碼,我重新打開了這個問題。

我們需要一些進口:

import Control.Monad.Trans 
import Control.Monad.Trans.Either 

然後通過應用EitherT每個IO動作,其通過返回一個Either發出錯誤信號,改變你的get功能:

-- get' :: EitherT String IO() 
get' id = do 
    d1 <- EitherT $ getData1 id 
    d2 <- EitherT $ getData2 (d2Id d1) 
    d3 <- EitherT $ getData3 (d3Id d2) 
    liftIO $ process d1 d2 d3 

注意process我們不使用EitherT。相反,我們使用liftIO,因爲process不表示錯誤。

GHC應該能夠推斷出類型簽名,所以你不需要提供它。

要運行新的版本,使用runEitherT將在IO-單子返回Either值:

doit :: String -> IO() 
doit id = do 
    res <- runEitherT (get' id) 
    case res of 
    Left err -> print err 
    Right d -> return() 
+0

爲什麼這個問題關閉? – Michael

+0

它被標記爲dup。這種問題之前已經出現過,但不完全在這種情況下,所以我決定重新打開它並添加一個答案。 – ErikR