给定代表电子邮件列表的IO [String]
:
λ: let emails = return ["foo@bar.com", "bip@bap.net"] :: IO [String]
一个自动无法删除电子邮件的功能:
λ: let deleteEmail email = return $ Left "failed" :: IO (Either String ())
然后我查看了如何为列表中的每封电子邮件尝试删除每封电子邮件。但是,当单个电子邮件无法删除时,我想停止,即类似于sequence
的行为。
λ: do { e <- emails; _ <- deleteEmail e; return e }
["foo@bar.com","bip@bap.net"]
λ: do { e <- emails; result <- deleteEmail e; return result }
Left "failed"
但是,通过查看第一个do
的输出,如果无法删除foo@bar.com
,则do
会继续尝试删除bip@bap.net
。
如何在第一次电子邮件删除失败时修改上述代码失败?
答案 0 :(得分:3)
有很多选择,但我会按照我的偏好顺序查看其中的三个。
EitherT
转换器而不是IO
而不是IO (Either a)
(您需要安装either
包)fail
Monad
实例中的IO
。throw
例外。答案 1 :(得分:2)
正如另一个答案所示,您可以使用ExceptT
或EitherT
monad变换器。 ExceptT
代表Either
以及基础monad(在本例中为IO),您可以使用lift
来评估操作。由于EihterT
形成一个monad(因此也适用),您可以使用sequence_
来组合所有电子邮件的删除,并在第一次失败时失败,例如。
import Control.Monad.Trans.Except
import Control.Monad.IO.Class (liftIO)
import Data.Foldable (sequence_)
getEmails :: IO [String]
getEmails = return ["foo@bar.com", "bip@bap.net", "something@example.com"]
deleteEmail :: String -> ExceptT String IO ()
deleteEmail email = do
liftIO $ putStrLn ("Deleting " ++ email)
if (isPrefixOf "bip" email)
then throwE email
else return ()
deleteAllEmails :: [String] -> ExceptT String IO ()
deleteAllEmails = sequence_ . map deleteEmail
doDelete :: [String] -> IO ()
doDelete emails = do
e <- runExceptT $ deleteAllEmails emails
case e of
Left err -> putStrLn $ "Failed: " ++ err
Right _ -> putStrLn "success!"
您可能还想考虑使用Maybe String
代替Either String ()
和相应的MaybeT
转换器。