2015-04-22 15 views
1

我很新的Haskell和我想了解使用此文档中创建单子解析器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类型中的函数,还是该计算的结果,还是其他?此外,第二行符号只是item,所以它只是运行item但不分配结果?最后,return (c1,c2)产生了什么?是Parser (String -> Maybe ((c1, c2)), String)还是Just (c1, c2)

回答

6

Parser型包装了使用Maybe,可以1)表示失败的函数和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)) 

注意,任何单子的最后一行做块必须具有相同类型的功能它是一个成员。由于return (c1, c2)的类型为Parser (Char, Char),因此必须takeThreeDropSecond,反之亦然。