似乎已经达成共识,你应该使用Parsec作为应用而不是monad。应用解析比monadic解析有什么好处?
monadic解析了吗?
答案 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
(>>=) = ???
所以是的,它基本上是风格的问题。我想如果你使用因为Applicative是一个比Monad严格更小的界面,这意味着return
和ap
,那么使用pure
和<*>
应该没有性能损失。<*>
有时可以比ap
更高度优化。 (但是使用聪明的GHC重写规则,无论如何都可以实现相同的优化。)
monadic解析了吗?
由于Monads是Applicatives的子集,我会得出结论,applicative parsing是monadic解析的一个子集。