我目前正在为我的一个小项目编写一些管道核心/ attoparsec 管道。我希望每个解析器都提供一个等待ByteString
解析器输入并产生任何解析值(重新启动解析器)的管道。如果没有错误处理,它将因此具有类似的类型
parserP :: Monad m => Parser a -> Pipe ByteString a m r
现在,我不确定如何处理解析错误。我目前的想法是:
- 将错误添加到返回类型(即返回一个值
Either ParseError r
而不是仅仅r
) - 要求monad提供错误处理机制(即要求monad接管管道实现
MonadError
) - 通过接管任何monad的管道
ErrorT e m a
来强制 monad 提供错误机制m
- 添加参数,让用户指定行为(类似
(ParseError -> P.Pipe ByteString a m r)
, 并在出现解析错误的情况下简单地绑定到由此提供的管道)
第一个解决方案似乎是错误的,因为使用管道的返回类型进行错误处理似乎是一种 hack。一方面,它使与管道的组合更加丑陋,并且似乎或多或少被最终解决方案所包含(除了可能失去让下游管道能够通过使用 tryAwait 并停止等待值来从错误中恢复的能力之外?)。
第二种解决方案似乎是错误的,尽管我不能完全说明原因。可能是因为它(可能?)还需要将 ParseError 转换为 monad 具有的任何错误类型的参数(除非我们希望要求 monad 实现MonadError ParseError
,这似乎会导致大量的簿记)。最后,我似乎不记得看到MonadError
过那么多,这表明使用它存在一些问题。
第三种解决方案适用于我的情况,因为管道将成为具有用户指定 monad(IO) 的管道的一部分,该管道不应该关心解析错误(它将网络数据解析为用户产生的格式指定类型)。但它看起来并不那么优雅,并且会再次(可能?)导致在任何其他上下文中使用时会产生大量的簿记。
我还没有真正考虑过最终的解决方案,但它似乎有些令人费解。
我将不胜感激对这个特殊案例的任何想法(如果我偏离并遗漏了一些明显的东西,我一点也不感到惊讶),以及任何(或多或少相关)关于管道错误处理讨论的参考( -core)/导管/interatee 等
编辑:另一种可能性可能是只采取一个单子动作(而不是一个完整的管道),尽管我不太确定它是否可能只是概括、专门化甚至等同于第四个。