使用管道(-core)的错误处理方法是什么?

Til*_*und 4 error-handling haskell

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

parserP :: Monad m => Parser a -> Pipe ByteString a m r
Run Code Online (Sandbox Code Playgroud)

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

  • 将错误添加到返回类型(即返回值Either ParseError r而不仅仅是r)
  • 要求monad提供错误处理机制(即要求monad管道被接管实现MonadError)
  • 强制单子通过采取管在提供错误的机制ErrorT e m a任何单子m
  • 添加参数,让用户指定行为(类似于(ParseError -> P.Pipe ByteString a m r),在解析错误的情况下简单地绑定到这样提供的管道)

第一个解决方案似乎是错误的,因为使用管道的返回类型进行错误处理似乎更像是一个黑客攻击.一方面,它使管道组成更加丑陋,似乎或多或少被最终解决方案所包含(除了可能失去让下游管道能够通过使用tryAwait从错误中恢复并停止等待值的能力?).

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

第三种解决方案适用于我的情况,因为管道将是管道的一部分,用户指定的monad(IO)不应该关心解析错误(它会将网络数据解析为产生用户的格式)指定的类型).但它看起来并不那么优雅,并且会再次(可能?)导致在任何其他环境中使用过很多书籍.

我没有真正考虑过最终的解决方案,但似乎有点费解.

对于这个特殊情况的任何想法我都会感激不尽(如果我离开并且遗漏了一些明显的东西,我就不会感到惊讶),以及任何(或多或少相关)关于管道中错误处理的讨论的参考( -core)/ conduit/interatee等

编辑:另一种可能性可能只采取一个monadic动作(而不是一个完整的管道),虽然我不太确定它是否可能只是概括,专业甚至相当于第四个.

Gab*_*lez 7

如果可以的话,我想我可以通过描述这样的选择来帮助组织每个人对此的想法.你要么:

  1. Pipe内的EitherT/ ErrorT:

    E e(管道abm)r

  2. / Pipe之外的层:EitherTErrorT

    管道ab(EitherT em)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)
Run Code Online (Sandbox Code Playgroud)

然后在PipeE中使用catchT和throwT来获取内容.

这种方法还有另一个优点,即您可以选择性地将其应用于管道的某些段,但是在您与其他管道组合之前,您有责任处理潜在的异常值.您可以使用这种灵活性为管道的不同阶段使用不同类型的异常值,或者根本不使用它来处理不能失败的阶段,并避免错误检查这些阶段的开销.

  • 一个很好的减少/简化的问题,以及在这种特殊情况下应用什么方法的一个很好的建议,完美! (2认同)