理解做符号和绑定
understanding do notation and bindings
我是 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 符号中的第二行只是 item
,那么它只是 运行 item
但不分配结果吗?最后,return (c1,c2)
会产生什么?是 Parser (String -> Maybe ((c1, c2)), String)
还是只是 Just (c1, c2)
?
Parser
类型包含一个函数,该函数可以 1) 使用 Maybe
表示失败和 2) returns 未通过 (a, String)
解析的剩余文本3) 一些被解析的值 a
,可以是任何值。 monad 实例是将它们连接在一起的管道。 return
实现围绕一个函数创建了一个 Parser
,该函数 1) 以 Just
成功,2) 不修改其输入文本,以及 3) 直接传递给它的值。 >>=
实现采用一个解析器和一个函数,然后 returns 一个由第一个 运行 p
创建的新解析器,然后根据该结果是通过还是失败 运行 f
.
在takeThreeDropSecond
中,第一个c1 <- item
说"parse the given using item
, assign its result to c1
, and feed the rest of the input forward"。这不会将 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)
有类型 Parser (Char, Char)
,所以必须 takeThreeDropSecond
,反之亦然。
我是 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 符号中的第二行只是 item
,那么它只是 运行 item
但不分配结果吗?最后,return (c1,c2)
会产生什么?是 Parser (String -> Maybe ((c1, c2)), String)
还是只是 Just (c1, c2)
?
Parser
类型包含一个函数,该函数可以 1) 使用 Maybe
表示失败和 2) returns 未通过 (a, String)
解析的剩余文本3) 一些被解析的值 a
,可以是任何值。 monad 实例是将它们连接在一起的管道。 return
实现围绕一个函数创建了一个 Parser
,该函数 1) 以 Just
成功,2) 不修改其输入文本,以及 3) 直接传递给它的值。 >>=
实现采用一个解析器和一个函数,然后 returns 一个由第一个 运行 p
创建的新解析器,然后根据该结果是通过还是失败 运行 f
.
在takeThreeDropSecond
中,第一个c1 <- item
说"parse the given using item
, assign its result to c1
, and feed the rest of the input forward"。这不会将 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)
有类型 Parser (Char, Char)
,所以必须 takeThreeDropSecond
,反之亦然。