我的代码结构如下例所示。我很确定应该有一种方法来更合理地构建它。我会假设任何(或错误)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

最佳答案

我正在重新打开这个问题,因为我认为看到它会有所帮助
如何转换这种特定的代码。

我们需要一些导入:

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

然后通过将 get 应用于每个通过返回 EitherT 发出错误信号的 IO-action 来转换您的 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

请注意,我们不将 EitherT 用于 process 。相反,我们使用 liftIO 因为 process 不会发出错误信号。

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

要运行新版本,请使用 runEitherT,它将在 IO-monad 中返回一个 Either 值:
doit :: String -> IO ()
doit id = do
  res <- runEitherT (get' id)
  case res of
    Left err -> print err
    Right d  -> return ()

关于haskell - 如何避免案件金字塔?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37796211/

10-10 23:52