我使用的许多 Parsec 组合器属于以下类型:
foo :: CharParser st Foo
CharParser
在这里定义为:
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 m a 是一个流类型为s,用户态类型为u的解析器, 底层 monad m 和返回类型 a。”底层的monad是什么?特别是,当我使用
CharParser
解析器时,它是什么?我看不到它在堆栈中的插入位置。在Monadic Parsing in Haskell 中使用列表 monad 从一个不明确的解析器返回多个成功的解析是否有关系?
Identity
。然而,ParsecT 与大多数 monad 转换器不同,因为它是
Monad
类的实例,即使类型参数
m
不是。如果您查看源代码,您会注意到实例声明中缺少“
(Monad m) =>
”。然后你问自己,“如果我有一个非平凡的 monad 堆栈,它会用在哪里?”
这个问题有三个答案:
uncons
流出的下一个标记:
class (Monad m) => Stream s m t | s -> t where
uncons :: s -> m (Maybe (t,s))
注意 uncons
需要一个
s
(令牌流
t
)并返回它的结果包装在你的 monad 中。这允许人们在获得下一个令牌的过程中甚至在过程中做一些有趣的事情。
lift (x :: m a) :: ParsecT s u m a
.
m
被
Identity
替换的地步)返回包含在这个 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 列表尾部以及标记流前面未使用的结果,因为我们从不回溯。在 parsec 中,给定组合解析器
a <|> b
,如果
a
消耗任何输入
b
将永远不会被评估。解决这个问题的方法是
try
如果
a
失败,它将把状态重置回原来的状态,然后评估
b
.您在评论中询问这是使用
Maybe
还是
Either
完成的。答案是“几乎但不完全是”。如果您查看低杠杆
run*
函数,您会看到它们返回一个代数类型,该类型告诉天气输入已被消耗,然后返回一个给出结果或错误消息的秒。这些类型的工作方式有点像
Either
,但即使是它们也不会被直接使用。与其进一步扩展,不如让您参考Antoine Latter 的帖子,它解释了这是如何工作的以及为什么这样做。
type Parsec s u = ParsecT s u Identity
所以答案是,当使用 CharParser 时,底层的 monad 是 Identity monad。