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

jij*_*esh 6 monads haskell ghc

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

do line <- getLine
   putStrLn line
Run Code Online (Sandbox Code Playgroud)

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

do line1 <- getLine
   putStrLn "enter second line"
   line2 <- getLine
   return (line1,line2)
Run Code Online (Sandbox Code Playgroud)

Pau*_*son 16

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

putStrLn :: String -> IO ()
Run Code Online (Sandbox Code Playgroud)

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

getLine >>= \line1 ->
putStrLn "enter second line" >>= \_ ->
getline >>= \line2 ->
return (line1, line2)
Run Code Online (Sandbox Code Playgroud)

实际上,已经定义了一个忽略返回值">>"的运算符.所以你可以把它重写为

getLine >>= \line1 ->
putStrLn "enter second line" >>
getline >>= \line2 ->
return (line1, line2)
Run Code Online (Sandbox Code Playgroud)

我不确定你是否也试图理解绑定运算符是如何菊花链式的.为了看到这个,让我在上面的例子中加上隐式括号和额外的缩进:

getLine >>= (\line1 ->
   putStrLn "enter second line" >> (
      getline >>= (\line2 ->
         return (line1, line2))))
Run Code Online (Sandbox Code Playgroud)

每个绑定操作符使用右侧的函数将值链接到左侧.该函数由"do"子句中的所有其余行组成.因此,通过lambda绑定的变量(第一行中的"line1")在整个子句的整个范围内.


Tra*_*own 7

对于这个具体的例子实际上你可以同时避免do>>=利用组合子来自Control.Applicative:

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

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

main = print =<< getInput
Run Code Online (Sandbox Code Playgroud)

哪个按预期工作:

travis@sidmouth% ./Main
hello
enter second line
world
("hello","world")
Run Code Online (Sandbox Code Playgroud)

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


fuz*_*fuz 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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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


sep*_*p2k 5

getLine >>= \line1 ->
putStrLn "enter second line" >>
getLine >>= \line2 ->
return (line1, line2)
Run Code Online (Sandbox Code Playgroud)

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