1

我对 haskell 非常陌生,我正在尝试了解用于在本文档中创建 Monadic 解析器的方法https://www.cs.nott.ac.uk/~gmh/pearl.pdf

为了正确理解它,我没有完全遵循它,而是尝试做一些不同的事情,因此,我最终得到了这段代码

newtype Parser a = Parser (String -> Maybe (a, String))

item :: Parser Char
item = Parser (\cs -> case cs of
            "" -> Nothing
            (c:cs) -> Just (c, cs))

getParser (Parser x) = x

instance Monad Parser where
    return x = Parser (\cs -> Just (x,cs))
    (Parser p) >>= f  = Parser (\cs -> let result = p cs in
                  case result of
                    Nothing -> Nothing
                    Just (c,cs') -> getParser (f c) cs')

takeThreeDropSecond :: Parser (Char, Char)
takeThreeDropSecond = do
    c1 <- item
    item
    c2 <- item
    return (c1, c2)

这似乎有效,但我很难理解 do 表示法中发生的事情。

例如; 在c1 <- item,分配给c1什么?它是包含在Parser类型中的函数,还是计算的结果,还是其他什么?此外, do 表示法中的第二行是 just item,所以它只是运行item但不分配结果吗?最后,return (c1,c2)产生什么?是Parser (String -> Maybe ((c1, c2)), String)还是只是Just (c1, c2)

4

1 回答 1

6

Parser类型封装了一个函数,该函数可以 1) 表示失败使用Maybe和 2) 返回未解析的剩余文本(a, String)以及 3)a已解析的某个值,可以是任何值。monad 实例是将它们联系在一起的管道。该return实现Parser围绕一个函数创建了一个 1) 成功的函数Just,2) 不修改其输入文本,以及 3) 直接传递给它的值。该>>=实现采用一个解析器和一个函数,然后返回一个通过首先运行创建的新解析器p,然后根据结果是通过还是失败运行f

takeThreeDropSecond中,首先c1 <- item说“使用 解析给定item的结果,将其结果分配给c1,并将其余的输入向前馈送”。这不会将item解析器内部的函数分配给c1,它会将内部函数运行的结果分配给item当前输入。然后你到达item,它使用 解析一个值item,不将它分配给任何东西,并将其余的输入向前馈送。接下来是c2 <- item,它与第一行基本相同,最后return (c1, c2)是 ,它将扩展为Parser (\cs -> Just ((c1, c2), cs))。这意味着return (c1, c2)具有类型Parser (Char, Char)。使用类型注释它将是

takeThreeDropSecond :: Parser (Char, Char)
takeThreeDropSecond = do
    (c1 :: Char) <- (item :: Parser Char)
    (item :: Parser Char)
    (c2 :: Char) <- (item :: Parser Char)
    (return (c1, c2) :: Parser (Char, Char))

请注意,任何 monadic do 块的最后一行必须与它所属的函数具有相同的类型。由于return (c1, c2)has type Parser (Char, Char),所以 must takeThreeDropSecond,反之亦然。

于 2015-04-22T17:48:49.560 回答