我是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)
?
答案 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)
,反之亦然。