我使用的许多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
因此Parsec
是ParsecT
的部分应用,其本身列出here类型:
data ParsecT s u m a
以及单词:
“ParsecT s u a a是一个流类型为s的解析器,用户状态类型为u, 底层monad m并返回类型a。“
潜在的monad是什么?特别是,当我使用CharParser
解析器时它是什么?我无法看到它在堆栈中的插入位置。是否与使用Monadic Parsing in Haskell中的列表monad从一个模糊的解析器返回多个成功的解析有关系?
答案 0 :(得分:8)
在您的情况下,基础monad是Identity
。然而,ParsecT与大多数monad变换器的不同之处在于,即使类型参数Monad
不是m
类,它也是(Monad m) =>
类的实例。如果您查看源代码,您会注意到实例声明中缺少“uncons
”。
那么你问自己,“如果我有一个非平凡的monad堆栈,它会在哪里使用?”
该问题有三个答案:
它用于class (Monad m) => Stream s m t | s -> t where
uncons :: s -> m (Maybe (t,s))
流中的下一个标记:
uncons
请注意,s
需要t
(令牌流lift (x :: m a) :: ParsecT s u m a
)并返回包含在monad中的结果。这允许人们在获取下一个令牌的过程中甚至在获取下一个令牌的过程中做有趣的事情。
它用于每个解析器的结果输出中。这意味着您可以创建不接触输入但在底层monad中执行操作的解析器,并使用组合器将它们绑定到常规解析器。换句话说,m
。
最后,RunParsecT和朋友的最终结果(直到你构建到Identity
被m
替换的点)返回包含在这个monad中的结果。
此monad与来自Monadic Parsing in Haskell的monad之间没有任何关系。在这种情况下,Hutton和Meijer指的是ParsecT本身的monad实例。 Parsec-3.0.0及ParsecT之外的事实已成为具有潜在monad的monad变换器,这与本文无关。
我认为您正在寻找的是可能的结果列表。在Hutton和Meijer中,解析器返回所有可能结果的列表,而Parsec固执地只返回一个。我想你正在查看结果中的a <|> b
,并且自己想一想结果列表必须隐藏在某处。事实并非如此。
出于效率原因,Parsec选择了Hutton和Meijer的结果列表中的第一个匹配结果。这让我们抛弃Hutton和Meijer列表中未使用的结果以及令牌流的前端,因为我们从不回溯。在parsec中,给定组合解析器a
,如果b
消耗任何输入try
将永远不会被评估。解决这个问题的方法是a
,如果b
失败则会将状态重置回原来的状态,然后评估Maybe
。
您在评论中询问是否使用Either
或run*
完成了此操作。答案是“几乎但不完全”。如果您查看低杠杆Either
函数,您会看到它们返回代数类型,它告诉天气输入被消耗,然后是第二个给出结果或错误消息。这些类型与{{1}}类似,但即使它们不直接使用。而不是进一步扩展这一点,我将引用Antantine Latter的the post来解释这是如何工作的以及为什么这样做。
答案 1 :(得分:6)
GenParser是根据Parsec而非ParsecT定义的。 Parsec依次定义为
type Parsec s u = ParsecT s u Identity
所以答案是当使用CharParser时,底层monad是Identity monad。