2016-06-13 3 views
3

私は以下の例のように構造化されたコードを持っています。私はそれがはるかに健全に構造化する方法があるはずだと確信しています。私はどちらか(またはエラー)モナドが助けになると思いますが、どこから始めるべきかわかりません。正しい方向に向かうための指針はありますか?ケースのピラミッドを避けるには?

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

私は特定のこの種のコードを変換するために 方法を見て参考になると思うので、私はこの質問を再開しています。

我々はいくつかの輸入必要がありますことを

-- 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 

注:

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

その後Eitherを返すことでエラーを通知し、各IO-アクションにEitherTを適用することによって、あなたのget変換関数をしますprocessにはEitherTを使用していません。代わりにliftIOを使用します。processはエラーを通知しないためです。

GHCは、あなたがそれを提供する必要はありませんので、型シグネチャを推測することができるはずです。

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

なぜ質問が閉じられました:?IO-モナドでEither値を返しますどのrunEitherTを使用し、新しいバージョンを実行するには

Michael

+0

ダブとしてマークされています。このような質問は以前から出てきましたが、この文脈で正確には出てこなかったので、私はそれを再開して回答を追加することにしました。 – ErikR

関連する問題