Parsing 底层Parsec单子

Parsing 底层Parsec单子,parsing,haskell,monads,monad-transformers,Parsing,Haskell,Monads,Monad Transformers,我使用的许多Parsec组合符的类型如下: foo :: CharParser st Foo CharParser定义为: 因此,CharParser是涉及GenParser的类型同义词,其本身定义为: GenParser是另一种类型的同义词,使用Parsec赋值,定义如下: 因此,Parsec是ParsecT的一个部分应用程序,其本身以类型列出: data ParsecT s u m a 除此之外: “ParsecT s u m a是流类型为s、用户状态类型为u的解析器, 基础monad

我使用的许多Parsec组合符的类型如下:

foo :: CharParser st Foo
CharParser
定义为:

因此,
CharParser
是涉及
GenParser
的类型同义词,其本身定义为:

GenParser
是另一种类型的同义词,使用
Parsec
赋值,定义如下:

因此,
Parsec
ParsecT
的一个部分应用程序,其本身以类型列出:

data ParsecT s u m a
除此之外:

“ParsecT s u m a是流类型为s、用户状态类型为u的解析器, 基础monad m和返回类型a。“


什么是潜在的单子?特别是,当我使用
CharParser
解析器时,它是什么?我看不到它在堆栈中的插入位置。使用中的列表单子从一个不明确的解析器返回多个成功的解析与此有关系吗?

GenParser是根据Parsec定义的,而不是ParsecT。Parsec反过来定义为

type Parsec s u = ParsecT s u Identity

因此,答案是当使用CharParser时,底层monad是Identity monad。

在您的例子中,底层monad是
Identity
。然而,ParsecT与大多数monad转换器不同,它是
monad
类的实例,即使类型参数
m
不是。如果查看源代码,您将注意到实例声明中缺少“
(Monad m)=>

然后你会问自己,“如果我有一个非平凡的monad堆栈,它会被用在哪里?”

这个问题有三个答案:

  • 它用于
    uncon
    流外的下一个令牌:

    class (Monad m) => Stream s m t | s -> t where
        uncons :: s -> m (Maybe (t,s))
    
    请注意,
    uncon
    接受一个
    s
    (令牌流
    t
    )并返回封装在monad中的结果。这允许一个人在获取下一个令牌的过程中做一些有趣的事情

  • 它用于每个解析器的结果输出。这意味着您可以创建不接触输入但在底层monad中执行操作的解析器,并使用组合器将它们绑定到常规解析器。换句话说,
    lift(x::ma)::ParsecT s u m a

  • 最后,RunParsecT和friends的最终结果(直到您构建到
    m
    Identity
    替换的位置)返回包装在这个monad中的结果

  • 这个单子和来自的单子之间没有关系。在本例中,Hutton和Meijer为ParsecT本身引用monad实例。事实上,在Parsec-3.0.0及以后的版本中,ParsecT已经成为一个具有底层monad的monad转换器,这与本文无关

    然而,我认为你正在寻找的是可能的结果列表。在Hutton和Meijer中,解析器返回所有可能结果的列表,而Parsec顽固地只返回一个。我想你看到的是结果中的
    m
    ,你在想,结果列表一定隐藏在那里的某个地方。事实并非如此

    出于效率考虑,Parsec选择了Hutton和Meijer的结果列表中的第一个匹配结果。让我们扔掉Hutton和Meijer列表尾部未使用的结果,以及令牌流的前端,因为我们从不回溯。在parsec中,给定组合解析器
    ab
    ,如果
    a
    使用任何输入
    b
    ,则永远不会对其求值。解决方法是
    try
    ,如果
    a
    失败,它会将状态重置回原来的状态,然后评估
    b


    您在评论中询问这是否是使用
    或者
    或者
    来完成的。答案是“几乎但不完全”。如果你看一下低级别的
    run*
    函数,你会发现它们返回一个代数类型,告诉天气输入被消耗,然后是一秒钟,给出结果或错误消息。这些类型的工作方式类似于
    ,但即使是它们也不能直接使用。我将向您介绍Antoine Later的文章,它将解释这是如何工作的,以及为什么这样做。

    谢谢,我编辑了我的问题以包括这一步。所以它是monad变压器的基础。我相信这与Hutton/Meijer论文中描述的模棱两可的解析没有关系。那么,list monad的使用是否出现在Parsec解析器中的任何地方?Parsec是唯一不含糊的吗?如果是这样的话,那是用
    编码的吗?也许是
    或者
    或者
    或者是
    ?底层的单子不是被parsec本身使用的,因此它不会影响歧义性。我想我想问的是Hutton/Meijer论文中列表单子之间的关系;以及在Parsec中使用的和类型。
    
    data ParsecT s u m a
    
    type Parsec s u = ParsecT s u Identity
    
    class (Monad m) => Stream s m t | s -> t where
        uncons :: s -> m (Maybe (t,s))