应用解析比monadic解析有什么好处?

时间:2011-10-22 19:01:00

标签: haskell monads parsec applicative

似乎已经达成共识,你应该使用Parsec作为应用而不是monad。应用解析比monadic解析有什么好处?

  • 性能
  • 抽象

monadic解析了吗?

5 个答案:

答案 0 :(得分:88)

monadic和applicative解析之间的主要区别在于如何处理顺序组合。对于应用解析器,我们使用(<*>),而对于monad,我们使用(>>=)

(<*>) :: Parser (a -> b) -> Parser a -> Parser b
(>>=) :: Parser a -> (a -> Parser b) -> Parser b

monadic方法更灵活,因为它允许第二部分的语法依赖于第一部分的结果,但我们在实践中很少需要这种额外的灵活性。

您可能认为拥有一些额外的灵活性不会伤害,但实际上它可以。它阻止我们在不运行它的情况下对解析器进行有用的静态分析。例如,假设我们想知道解析器是否可以匹配空字符串,以及匹配中可能的第一个字符是什么。我们想要功能

empty :: Parser a -> Bool
first :: Parser a -> Set Char

使用适用的解析器,我们可以轻松回答这个问题。 (我在这里作弊。想象一下,我们的候选解析器“语言”中有一个与(<*>)(>>=)对应的数据构造函数。)

empty (f <*> x) = empty f && empty x
first (f <*> x) | empty f   = first f `union` first x
                | otherwise = first f

然而,使用monadic解析器,我们不知道第二部分的语法是什么,而不知道输入。

empty (x >>= f) = empty x && empty (f ???)
first (x >>= f) | empty x   = first x `union` first (f ???)
                | otherwise = first x

通过允许更多,我们能够减少推理。这类似于动态和静态类型系统之间的选择。

但这有什么意义呢?我们可以使用这些额外的静态知识吗?好吧,我们可以使用它来避免在LL(1)解析中通过将下一个字符与每个备选的first集进行比较来回溯。我们还可以通过检查两个备选方案的first组是否重叠来静态地确定这是否含糊不清。

另一个例子是它可以用于错误恢复,如S. Doaitse Swierstra和Luc Duponcheel撰写的文章Deterministic, Error-Correcting Combinator Parsers所示。

然而,通常,您正在使用的解析库的作者已经在应用和一元解析之间进行了选择。当像Parsec这样的库暴露这两个接口时,选择使用哪个接口纯粹是一种风格。在某些情况下,应用代码比monadic代码更容易阅读,有时它反过来。

答案 1 :(得分:13)

如果解析器纯粹是应用程序,则可以在运行之前分析其结构并“优化”它。如果一个解析器是monadic,它基本上是一个图灵完备的程序,并且执行几乎任何有趣的分析都等同于解决暂停问题(即,不可能)。

哦,是的,这也有风格差异......

答案 2 :(得分:11)

我可以看到更喜欢应用解析器而不是monadic解析器的主要原因与在任何上下文中更喜欢应用代码而非monadic代码的主要原因相同:功能较弱,应用程序使用起来更简单。

这是一个更通用的工程格言的实例:使用最简单的工具来完成工作。当小车可以使用时,不要使用叉车。不要使用台锯切出优惠券。当IO中的代码纯粹时,不要编写代码。把事情简单化。

但有时,你需要 Monad的额外力量。确切的迹象是,当您需要根据到目前为止计算的内容更改计算过程时。在解析术语时,这意味着根据到目前为止已解析的内容确定如何解析接下来的内容;换句话说,你可以用这种方式构造上下文敏感的语法。

答案 3 :(得分:4)

使用Parsec,使用Applicative的好处就是风格。 Monad的优势在于它更强大 - 您可以实现上下文敏感的解析器。

如果仅使用适用的话,Doaitse Swierstra的UU解析会更有效。

答案 4 :(得分:2)

Monads严格来说是一种更具特色的抽象而非Applicatives。你可以写

instance (Monad m) => Applicative m where
  pure  = return
  (<*>) = ap

但是没有办法写

instance (Applicative a) => Monad a where
  return = pure
  (>>=) = ???

所以是的,它基本上是风格的问题。我想如果你使用returnap,那么使用pure<*>应该没有性能损失因为Applicative是一个比Monad严格更小的界面,这意味着<*>有时可以比ap更高度优化。 (但是使用聪明的GHC重写规则,无论如何都可以实现相同的优化。)

  

monadic解析了吗?

由于Monads是Applicatives的子集,我会得出结论,applicative parsing是monadic解析的一个子集。