如何直接访问 Parsecs 输入流

How to access Parsecs Input Stream directly

我想直接访问 parsecs 输入流,这可以使用 getParserState 完成。要从流中读取,提供了 uncons 方法。但是我(像往常一样)面临一个与类型相关的问题。所以这是我的解析函数:

myParser :: (Stream s m Char) => ParsecT s (ShiftedState u s) m String
myParser = do
  s <- P.getParserState
  let i = stateInput s
  let x = uncons i
  return ""

我收到以下错误。

Could not deduce (Stream s m0 Char)
        arising from a use of ‘uncons’

问题很简单,我不太清楚错误的确切含义。我认为 uncons 在底层 monad 中运行,而不是在 ParsecT monad 中运行。但是我不知道怎么解除(?)它。

基本上我想知道如何使用 uncons 并从流中读取。现在请不要担心 是否应该 这样做...这基本上是我对 monads 工作原理的理解 xD

这是正在发生的事情:uncons 需要在某种 monad 中 运行。您可以通过其类型签名看到这一点:uncons :: Stream s m t => s -> m (Maybe (t, s))。但是你只是在做 let x = uncons i —— 所以 x 是一个单子计算,returns 是 uncons i 的结果,但你没有指定 哪个 monad 这个monadic计算在运行ning里面。 碰巧知道你“想要”uncons i到运行在你的monad m(满足Stream s m Char),但是GHC不知道这个。所以 GHC 假设这里 uncons i :: m0 (Maybe (t, s)),其中 m0 是一些任意的单子。因此 GHC 会产生您看到的错误,因为 uncons 需要满足约束 Stream s m0 Char 才能使用它,但是对于任何随机 m0.[= 不一定满足该约束。 37=]

如何解决?好吧,您已经知道整个计算的类型是 ParsecT s (ShiftedState u s) m Char,并且 m 满足实例 Stream s m Char。事实证明,有一个函数 lift :: Monad m => m a -> ParsecT s u m a 可以将单子计算 m a 提升为秒差距计算 ParsecT s u m a。所以你可以将你的函数重写为:

myParser :: (Stream s m Char) => ParsecT s (ShiftedState u s) m String
myParser = do
  s <- P.getParserState
  let i = stateInput s
  x <- lift (uncons i)
  return ""

我还没有测试过,但如果我没有犯任何错误,这应该是可行的。

(另一种解决方案是简单地给 x 一个类型签名:

let x :: m (Maybe (Char, s))
    x = uncons i

这会强制 x 具有您想要的类型。现在 GHC 可以查看 ms 都满足相关约束,因此不会产生该错误。但这需要 ScopedTypeVariables 语言扩展来编译,并且比以前的解决方案要优雅得多。)