理解符号和绑定

yas*_*sar 1 monads haskell do-notation

我是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)
Run Code Online (Sandbox Code Playgroud)

这似乎有效,但我很难跟踪记号中发生的事情.

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

bhe*_*ilr 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))
Run Code Online (Sandbox Code Playgroud)

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