理解符号和绑定

时间:2015-04-22 17:25:52

标签: haskell monads 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)

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

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

1 个答案:

答案 0 :(得分:6)

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

f中,首先takeThreeDropSecond说&#34;使用c1 <- item解析给定的内容,将其结果分配给item,并向前提供其余输入&# 34 ;.这不会将c1解析器中的函数分配给item,它会将c1内的函数运行结果分配给当前输入。然后,您到达item,使用item解析一个值,不会将其分配给任何内容,并向前提供其余输入。接下来,您到达item,它与第一行基本相同,最后到c2 <- item,它将扩展为return (c1, c2)。这意味着Parser (\cs -> Just ((c1, c2), cs))的类型为return (c1, c2)。使用类型注释,它将是

Parser (Char, Char)

请注意,任何monadic do块的最后一行必须与它所属的函数具有相同的类型。由于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)) 的类型为return (c1, c2),因此必须Parser (Char, Char),反之亦然。