9

我知道下面的“do”符号的“bind”函数等价于 getLine >>= \line -> putStrLn

do line <- getLine
   putStrLn line

但是下面的符号如何等同于绑定函数?

do line1 <- getLine
   putStrLn "enter second line"
   line2 <- getLine
   return (line1,line2)
4

4 回答 4

19

我认为您正在尝试查看如何绑定“putStrLn”的结果。答案是 putStrLn 的类型:

putStrLn :: String -> IO ()

请记住,“()”是单位类型,它有一个值(也写成“()”)。所以你可以用完全相同的方式绑定它。但是由于您不使用它,因此您将其绑定到“无关”值:

getLine >>= \line1 ->
putStrLn "enter second line" >>= \_ ->
getline >>= \line2 ->
return (line1, line2)

碰巧的是,已经定义了一个忽略返回值“>>”的运算符。所以你可以把它重写为

getLine >>= \line1 ->
putStrLn "enter second line" >>
getline >>= \line2 ->
return (line1, line2)

我不确定您是否也想了解绑定运算符是如何以菊花链形式连接的。为了看到这一点,让我在上面的示例中放置隐式括号和额外的缩进:

getLine >>= (\line1 ->
   putStrLn "enter second line" >> (
      getline >>= (\line2 ->
         return (line1, line2))))

每个绑定运算符将左侧的值与右侧的函数链接起来。该函数由“do”子句中的所有其余行组成。因此,通过 lambda 绑定的变量(第一行中的“line1”)在整个子句其余部分的范围内。

于 2010-09-14T16:56:13.987 回答
7

对于这个特定的示例,您实际上可以避免两者do,也可以>>=使用以下组合子Control.Applicative

module Main where
import Control.Applicative ((<$>), (<*>), (<*))

getInput :: IO (String, String)
getInput = (,) <$> getLine <* putStrLn "enter second line" <*> getLine

main = print =<< getInput

哪个按预期工作:

travis@sidmouth% ./Main
hello
enter second line
world
("hello","world")

一开始看起来有点奇怪,但在我看来,应用风格一旦你习惯了就会感觉很自然。

于 2010-09-14T18:04:27.723 回答
6

我强烈建议您阅读Real-World haskell 一书中的Desugaring of Do-blocks一章。它告诉你,你们都错了。对于程序员来说,这是使用 lambda 的自然方式,但是 do-block 是使用函数实现的——如果发生模式处理失败——将调用fail相应 monad 的实现。

例如,您的情况如下:

let f x =
        putStrLn "enter second line" >>
        let g y = return (x,y)
            g _ = fail "Pattern mismatched"
        in getLine >>= g
    f _ = fail "Pattern mismatched"
in getLine >>= f

在这种情况下,这可能完全无关紧要。但是考虑一些涉及模式匹配的表达式。此外,您可以将此效果用于一些特殊的东西,例如,您可以执行以下操作:

oddFunction :: Integral a => [a] -> [a]
oddFunctiond list = do
  (True,y) <- zip (map odd list) list
  return y

这个函数会做什么?您可以将此语句作为使用列表元素的规则来阅读。第一条语句将列表的一个元素绑定到 var y,但前提是 y 是奇数。如果 y 是偶数,则会发生模式匹配失败fail并将被调用。在 Lists 的 monad 实例中,fail就是[]. 因此,该函数从列表中删除所有偶数元素。

(我知道,oddFunction = filter odd这样做会更好,但这只是一个例子)

于 2010-09-15T04:54:03.717 回答
5
getLine >>= \line1 ->
putStrLn "enter second line" >>
getLine >>= \line2 ->
return (line1, line2)

通常foo <- bar变成bar >>= \foo ->baz变成baz >>(除非它是 do-block 的最后一行,在这种情况下它只是停留baz)。

于 2010-09-14T16:56:03.537 回答