转换带有两个以上操作的“do”表示法以使用bind函数

时间:2010-09-14 16:42:32

标签: haskell ghc monads

我知道以下“do”表示法的“绑定”功能相当于getLine >>= \line -> putStrLn

do line <- getLine
   putStrLn line

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

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

4 个答案:

答案 0 :(得分:16)

我认为你试图看看如何绑定“putStrLn”的结果。答案是putStrLn的类型:

putStrLn :: String -> IO ()

请记住,“()”是单位类型,它有一个值(也写成“()”)。所以你可以用完全相同的方式绑定它。但是既然你没有使用它,你将它绑定到“不关心”值:

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

实际上,已经定义了一个忽略返回值的运算符“&gt;&gt;”。所以你可以把它重写为

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”)在整个子句的其余部分的范围内。

答案 1 :(得分: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")

起初看起来有点奇怪,但在我看来,一旦你习惯了它,应用风格会非常自然。

答案 2 :(得分:3)

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

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

答案 3 :(得分:3)

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

例如,您的情况就像:

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。在列表的monad实例中,fail只是[]。因此,该函数会从列表中删除所有偶数元素。

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