Haskell IO上的快速失败[字符串]

Haskell IO上的快速失败[字符串],haskell,Haskell,给定一个表示电子邮件列表的IO[String]: λ: let emails = return ["foo@bar.com", "bip@bap.net"] :: IO [String] 以及自动删除电子邮件失败的功能: λ: let deleteEmail email = return $ Left "failed" :: IO (Either String ()) 然后我研究了如何针对列表中的每封电子邮件尝试删除每封电子邮件。但是,当一封电子邮件无法删除时,我希望停止,即类似于序列的行为

给定一个表示电子邮件列表的
IO[String]

λ: let emails = return ["foo@bar.com", "bip@bap.net"] :: IO [String]
以及自动删除电子邮件失败的功能:

λ: let deleteEmail email = return $ Left "failed" :: IO (Either String ())
然后我研究了如何针对列表中的每封电子邮件尝试删除每封电子邮件。但是,当一封电子邮件无法删除时,我希望停止,即类似于
序列
的行为

λ: do { e <- emails; _ <- deleteEmail e; return e }
["foo@bar.com","bip@bap.net"]

λ: do { e <- emails; result <- deleteEmail e; return result }
Left "failed"

λ:do{e有很多选择,但我会按照我的偏好顺序看其中三个

  • 使用
    EitherT
    transformer over
    IO
    而不是
    IO(任一a)
    (您需要安装
    任一
    软件包)
  • 使用
    IO
    Monad
    实例中的
    fail
  • 抛出一个异常

另一个答案表明,您可以使用
ExceptT
EitherT
单子转换器。
ExceptT
表示一个
以及一个基本单子(在本例中为IO),在该单子中,您可以使用
提升
评估动作。因为
EihterT
形成单子(因此适用)您可以使用
序列
组合删除所有电子邮件,并在第一次失败时失败,例如:

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!"
import Control.Monad.Trans.Except
导入控制.Monad.IO.Class(liftIO)
导入数据。可折叠(序列)
getEmails::IO[字符串]
getEmails=返回[“foo@bar.com", "bip@bap.net", "something@example.com"]
deleteEmail::String->Except字符串IO()
deleteEmail=do
liftIO$putStrLn(“删除”++电子邮件)
如果(isPrefixOf“bip”电子邮件)
然后通过电子邮件
否则返回()
deleteAllEmails::[String]->字符串IO()除外
deleteAllEmails=序列\映射deleteMail
Dodelite::[String]->IO()
doDelete电子邮件=do
e putStrLn$“失败:”++错误
右->putStrLn“成功!”

您可能还想考虑使用<代码>字符串而不是<代码>字符串()/代码>和相应的<代码> MaybeT < /代码>转换器。

表达式“>代码> do.{另一件事是<代码> e <代码>实际上并不是指单个电子邮件,而是整个列表。使用
时自动“拆分”您可以在transformers中使用
ExceptT
(这很可能已经是一种依赖关系)代替
EitherT
。嗨,巴特克-你能用一些代码示例来补充你的答案吗?我不熟悉monad transformers。谢谢编辑-第三个选项,
抛出异常
不是惯用的,是吗?@KevinMeredith我当然更喜欢
ExceptT
而不是
throwIO
(不要在
IO
中使用
throw
!);但是有时候例外情况正是医生所要求的,所以我不认为我会认可“
throwIO
不是惯用的”。我可能会被说服认可”
throw
不是惯用的“。上次我检查了Haskell中至少有7种处理错误的方法,我不知道在
IO
monad中是否有任何一种被认为是“非惯用的”
fail
,最后也抛出了一个异常,并且由于其他原因有些不推荐。我使用
的唯一原因是…()
是指
..
可以是ADT。使用
可能字符串
大小写可能无法提供
的含义(除非
与之一起使用)。注意-我知道我使用了
字符串,但它不是ADT。