我知道下面的“do”符号的“bind”函数等价于 getLine >>= \line -> putStrLn
do line <- getLine
putStrLn line
但是下面的符号如何等同于绑定函数?
do line1 <- getLine
putStrLn "enter second line"
line2 <- getLine
return (line1,line2)
我认为您正在尝试查看如何绑定“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”)在整个子句其余部分的范围内。
对于这个特定的示例,您实际上可以避免两者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")
一开始看起来有点奇怪,但在我看来,应用风格一旦你习惯了就会感觉很自然。
我强烈建议您阅读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
这样做会更好,但这只是一个例子)
getLine >>= \line1 ->
putStrLn "enter second line" >>
getLine >>= \line2 ->
return (line1, line2)
通常foo <- bar
变成bar >>= \foo ->
和baz
变成baz >>
(除非它是 do-block 的最后一行,在这种情况下它只是停留baz
)。