【发布时间】: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)中。但我不太确定如何对这个问题做出客观的回答。 -
@DanielWagner 我同意这似乎是上述最好的解决方案。问题是,我总是喜欢引入额外的类型类。特别是对于任何不只是美化运算符重载的东西。
-
@DanBurton 我才意识到我之前忘了说谢谢! :)