如何避免案件金字塔?

Łuk*_*asz 5 haskell either

我的代码结构如下例所示。我很确定应该有一种方法可以更合理地构建它。我认为 Either (or Error) 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
Run Code Online (Sandbox Code Playgroud)

Eri*_*ikR 4

我重新提出这个问题是因为我认为了解如何转换这种特定代码会很有帮助。

我们需要一些进口:

import Control.Monad.Trans
import Control.Monad.Trans.Either
Run Code Online (Sandbox Code Playgroud)

然后get通过应用EitherT到每个 IO 操作来转换您的函数,该操作通过返回一个信号来指示错误Either:

-- 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
Run Code Online (Sandbox Code Playgroud)

请注意,我们不使用EitherTfor process。相反,我们使用liftIOsinceprocess并不表示错误。

GHC 应该能够推断类型签名,因此您无需提供它。

要运行新版本,请使用它将在 IO-monad 中runEitherT返回一个值:Either

doit :: String -> IO ()
doit id = do
  res <- runEitherT (get' id)
  case res of
    Left err -> print err
    Right d  -> return ()
Run Code Online (Sandbox Code Playgroud)