【问题标题】:Underlying Parsec Monad底层 Parsec Monad
【发布时间】:2011-11-08 16:56:16
【问题描述】:

我使用的许多 Parsec 组合器属于以下类型:

foo :: CharParser st Foo

CharParser 定义为 here

type CharParser st = GenParser Char st

CharParser 因此是涉及GenParser 的类型同义词,其本身将here 定义为:

type GenParser tok st = Parsec [tok] st

GenParser 是另一个类型的同义词,使用Parsec 分配,将here 定义为:

type Parsec s u = ParsecT s u Identity

所以ParsecParsecT 的部分应用,它本身列出了here 类型:

data ParsecT s u m a

连同这些词:

"ParsecT s u m a 是一个流类型为 s,用户状态类型为 u 的解析器, 底层 monad m 和返回类型 a。"

什么是底层的monad?特别是,当我使用 CharParser 解析器时会发生什么?我看不到它在堆栈中的插入位置。是否与使用 Monadic Parsing in Haskell 中的 list monad 从不明确的解析器返回多个成功的解析有关?

【问题讨论】:

    标签: parsing haskell monads monad-transformers


    【解决方案1】:

    在您的情况下,底层 monad 是 Identity。然而 ParsecT 与大多数 monad 转换器的不同之处在于它是 Monad 类的实例,即使类型参数 m 不是。如果您查看源代码,您会注意到实例声明中缺少“(Monad m) =>”。

    那么你问自己,“如果我有一个非平凡的 monad 堆栈,它会用在哪里?”

    这个问题有三个答案:

    1. 用于uncons流中的下一个标记:

      class (Monad m) => Stream s m t | s -> t where
          uncons :: s -> m (Maybe (t,s))
      

      请注意,uncons 采用 s(标记流 t)并返回包装在您的 monad 中的结果。这允许人们在获得下一个令牌的过程中甚至在获得下一个令牌的过程中做有趣的事情。

    2. 它用于每个解析器的结果输出中。这意味着您可以创建不接触输入但在底层 monad 中执行操作的解析器,并使用组合器将它们绑定到常规解析器。换句话说,lift (x :: m a) :: ParsecT s u m a

    3. 最后,RunParsecT 和朋友们的最终结果(直到你积累到mIdentity 替换)返回包装在这个 monad 中的结果。

    这个monad和来自Monadic Parsing in Haskell的monad没有关系。在这种情况下,Hutton 和 Meijer 指的是 ParsecT 本身的 monad 实例。在 Parsec-3.0.0 及更高版本中 ParsecT 已成为具有底层 monad 的 monad 转换器这一事实与本文无关。

    我认为您正在寻找的是可能结果列表的位置。在 Hutton 和 Meijer 中,解析器返回所有可能结果的列表,而 Parsec 固执地只返回一个。我认为您正在查看结果中的m,并认为结果列表必须隐藏在某处。不是。

    Parsec 出于效率的原因,选择在 Hutton 和 Meijer 的结果列表中首选第一个匹配结果。这让它在 Hutton 和 Meijer 列表的尾部以及令牌流的前面丢弃未使用的结果,因为我们从不回溯。在解析中,给定组合解析器 a <|> b,如果 a 使用任何输入 b 将永远不会被评估。解决这个问题的方法是try,如果a 失败然后评估b,它会将状态重置回原来的位置。

    您在 cmets 中询问这是使用 Maybe 还是 Either 完成的。答案是“几乎但不完全”。如果您查看低杆run* 函数,您会看到它们返回一个代数类型,告诉天气输入已被消耗,然后是第二个给出结果或错误消息。这些类型的工作方式有点像Either,但即使它们也不直接使用。与其进一步扩展,我将向您推荐 Antoine Latter 的 the post,它解释了它是如何工作的以及为什么这样做。

    【讨论】:

      【解决方案2】:

      GenParser 是根据 Parsec 而不是 ParsecT 定义的。 Parsec 又被定义为

      type Parsec s u = ParsecT s u Identity
      

      所以答案是,当使用 CharParser 时,底层 monad 是 Identity monad。

      【讨论】:

      • 谢谢,我编辑了我的问题以包含该步骤。所以它是monad转换器的基础。我相信这与 Hutton/Meijer 论文中描述的模棱两可的解析没有关系。那么 list monad 的使用是否出现在 Parsec 解析器中的任何地方? Parsec 是否只是明确的?如果是这样,是使用MaybeEither 编码的吗?
      • parsec 本身不使用底层 monad,因此不会影响歧义。
      • 我想我想问的是 Hutton/Meijer 论文中 list monad 之间的关系;以及 Parsec 中使用的 ConsumedReply 类型。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-04
      • 2017-02-05
      • 2013-11-16
      • 2015-06-02
      • 1970-01-01
      相关资源
      最近更新 更多