底层 Parsec Monad

问题描述 投票:0回答:2

我使用的许多 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 从一个不明确的解析器返回多个成功的解析是否有关系?

parsing haskell monads monad-transformers
2个回答
9
投票
在你的例子中,底层的单子是

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 和朋友们的最终结果(直到您构建到

    m

    Identity
     替换的地步)返回包含在这个 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 列表尾部以及标记流前面未使用的结果,因为我们从不回溯。在 parsec 中,给定组合解析器

a <|> b

,如果 
a
 消耗任何输入 
b
 将永远不会被评估。解决这个问题的方法是 
try
 如果 
a
 失败,它将把状态重置回原来的状态,然后评估 
b
.

您在评论中询问这是使用

Maybe

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


6
投票
GenParser 是根据 Parsec 而不是 ParsecT 定义的。秒差距又定义为

type Parsec s u = ParsecT s u Identity

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

© www.soinside.com 2019 - 2024. All rights reserved.