【问题标题】:What approach to error handling to use with pipes(-core)?与管道(-core)一起使用的错误处理方法是什么?
【发布时间】:2012-07-09 20:49:41
【问题描述】:

我目前正在为我的一个小项目编写一些管道核心/attoparsec 管道。我希望每个解析器都提供一个等待ByteString 输入到解析器并产生任何解析值(重新启动解析器)的管道。如果没有错误处理,它将因此具有类似

的类型
parserP :: Monad m => Parser a -> Pipe ByteString a m r

现在,我不确定如何处理解析错误。我目前的想法是:

  • 将错误添加到返回类型(即在Either ParseError r 中返回一个值,而不仅仅是r
  • 要求 monad 提供错误处理机制(即要求接管管道的 monad 实现MonadError
  • 通过为 any monad m 接管 ErrorT e m a 来强制 monad 提供错误机制
  • 添加参数,让用户指定行为(类似于(ParseError -> P.Pipe ByteString a m r),并在出现解析错误时简单地绑定到提供的管道)

第一个解决方案似乎是错误的,因为使用管道的返回类型进行错误处理似乎是一种 hack。一方面,它使与管道的组合变得更加丑陋,并且似乎或多或少地被最终解决方案所包含(除了可能失去让下游管道能够通过使用 tryAwait 并停止等待值来从错误中恢复的能力之外) ?)。

第二个解决方案似乎是错误的,但我不能完全确定原因。可能是因为它(可能?)还需要将 ParseError 转换为 monad 具有的任何错误类型的参数(除非我们希望要求 monad 实现MonadError ParseError,这似乎会导致大量簿记) .最后,我似乎不记得看到过那么多MonadError,这表明使用它存在一些问题。

第三种解决方案适用于我的情况,因为管道将成为具有用户指定 monad(IO) 的管道的一部分,它不应该关心解析错误(它将网络数据解析为产生的格式到用户指定的类型)。但它看起来并不那么优雅,并且(可能?)再次用于任何其他环境时会导致大量的簿记。

我还没有真正考虑过最终的解决方案,但似乎有些令人费解。

如果我对这个特殊案例有任何想法,我将不胜感激(如果我偏离并遗漏了一些明显的东西,我一点也不感到惊讶),以及任何(或多或少相关)关于错误处理的讨论的参考在管道(-core)/conduits/interatee 等中

编辑: 另一种可能性可能是只采取一个单子动作(而不是一个完整的管道),尽管我不太确定它是否只是泛化、专门化甚至等同于第四个。

【问题讨论】:

  • 我会注意到提案 3(使用 ErrorT e m a)完全包含在我个人最喜欢的提案 2(使用 MonadError)中。但我不太确定如何对这个问题做出客观的回答。
  • 我已经给 Gabriel 发了电子邮件通知他这件事,因为他是 pipeserrors 包的作者;大概他一直在考虑这两件事,可能同时考虑。
  • @DanielWagner 我同意这似乎是上述最好的解决方案。问题是,我总是喜欢引入额外的类型类。特别是对于任何不只是美化运算符重载的东西。
  • @DanBurton 我才意识到我之前忘了说谢谢! :)

标签: haskell error-handling


【解决方案1】:

如果可以的话,我想我可以通过这样描述选择来帮助整理每个人的想法。你要么:

  1. EitherT/ErrorT 内的层 Pipe

    EitherT e (管道 a b m) r

  2. Pipe 之外的层 EitherT/ErrorT

    管道 a b (EitherT e m) r

你想要前一种方法,它还有一个很好的属性,你可以让它成为 MonadError 的实例(如果这是你的事)。

要了解这两种方法之间的区别,第二种方法会在整个管道级别引发错误。第一个允许在单个管道的粒度上进行错误处理并正确处理组合管道。

现在是一些代码。如果您不介意,我会使用 EitherT,因为我更喜欢它:

import Control.Error
import Control.Pipe

type PipeE e a b m r = EitherT e (Pipe a b m) r

runPipeE = runPipe . runEitherT

p1 <?< p2 = EitherT (runEitherT p1 <+< runEitherT p2)

然后只需在 PipeE 中使用 catchT 和 throwT 即可。

这种方法还有另一个优点,即您可以有选择地将其应用于管道的某些部分,但随后您需要负责处理潜在的异常值,然后再将其与其他管道组合。您可以利用这种灵活性为管道的不同阶段使用不同类型的异常值,或者对不会失败的阶段完全不使用它,从而避免对这些阶段进行错误检查的开销。

【讨论】:

  • 对问题的一个很好的简化/简化,以及在这种特殊情况下应用什么方法的一个很好的建议,完美!
猜你喜欢
  • 2017-04-11
  • 2019-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-12
  • 2017-11-13
相关资源
最近更新 更多