我的代码结构如下例所示。我很确定应该有一种方法可以更合理地构建它。我认为 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)
我重新提出这个问题是因为我认为了解如何转换这种特定代码会很有帮助。
我们需要一些进口:
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)