在 IO monad 中使用 parsec 解析器

Using a parsec parser in the IO monad

我已经使用 Parsec 定义了一个解析器,对于某些 a,它的类型为 Parsec Text () a。我还有一个 "deal with this chunk" 函数,它将我解析的内容写入文件并且类型为 a -> IO ()。它解析的文件格式意味着我们可以相当频繁地返回到 "top level"。

有没有办法将我的原始解析器和 "lift it" 带入 IO monad?我正在想象具有以下类型签名的东西:

liftParser :: Parsec Text () a -> (a -> IO ()) -> ParsecT Text () IO ()

其中第一个参数是纯解析器,第二个参数是 "do something with the thing I parsed" 函数。

显然,我也可以通过在 IO 中重新定义我的原始解析器来避免我需要的东西,但这意味着我的单元测试看起来很糟糕,而且感觉像是错误的方法。

此外,我不能做一些疯狂的事情,比如调用 runParserT,因为那样会丢失源位置信息 - 如果输入的第 1000 行有错误,我希望错误消息说所以。

那么有没有办法做到这一点,如果有,怎么做?另外,这是明智的做法吗?我想我至少设法避免累积输出数据。而且,假设我管理这样的事情,我是否应该期望 Parsec 设法丢弃已经处理过的输入数据?

只是为了让这个问题可以被标记为已回答:luqui 上面的评论解释了如何做。

诀窍是多态地定义所有解析器,类型为 ParsecT Text () m(定义最终看起来都像 thing :: Monad m => Parsec Text () m MyType)。然后你可以在测试平台中用m实例化identity monad并等于IO它们被使用的地方。