在 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
它们被使用的地方。
我已经使用 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
它们被使用的地方。