Haskell中如何将do表示法转换为bind(>>=)表示法?
把Haskell Do表示法转换为Bind(>>=)表示法的分步指南
嘿,很高兴看到你已经在动手尝试转换do表示法了!你已经走对了方向,只是差最后一步把line1的打印语句加进去而已。咱们一步步拆解这个问题,帮你彻底搞清楚转换的逻辑。
首先,先回顾一下do表示法和>>=(bind)的核心对应关系:
- 每一行
x <- IOAction,本质上就是把IOAction的结果绑定到变量x,然后把后续的所有代码放到一个lambda里,通过>>=传递这个变量。 - 对于没有绑定变量的IO动作(比如
putStrLn ...),我们可以用>>来连接它和后面的动作——>>其实是>>=的简化版,专门用来处理“忽略前一个动作的返回值,直接执行下一个动作”的场景。
现在来看你的原代码:
rev2lines :: IO () rev2lines = do line1 <- getLine line2 <- getLine putStrLn (reverse line2) putStrLn (reverse line1)
我们来逐行转换:
第一步:处理第一行 line1 <- getLine
这一行之后的所有代码(剩下的三行)都是拿到line1之后要执行的逻辑,所以我们先把getLine和一个捕获line1的lambda绑定:
rev2lines = getLine >>= (\line1 -> -- 捕获line1,后续逻辑在这里面 -- 剩下的do块内容放这里 )
第二步:处理第二行 line2 <- getLine
在刚才的lambda里面,我们继续处理line2的绑定,同样把后续的两个putStrLn放到捕获line2的lambda里:
rev2lines = getLine >>= (\line1 -> getLine >>= (\line2 -> -- 捕获line2,两个打印语句在这里面 -- 两个putStrLn放这里 ) )
第三步:处理最后两个打印语句
现在,我们需要把两个putStrLn按顺序执行。因为第一个putStrLn返回IO (),我们不需要它的返回值,直接执行第二个putStrLn就可以,所以用>>连接它们:
rev2lines = getLine >>= (\line1 -> getLine >>= (\line2 -> putStrLn (reverse line2) >> putStrLn (reverse line1) ) )
这样就完成了完整的转换!你之前的代码只写到了第一个putStrLn,没有把第二个putStrLn包含到最内层的lambda里——而因为line1是在最外层lambda捕获的,所以内层的lambda完全可以访问它,只要把第二个打印语句也放在里面就行。
额外补充:理解>>和>>=的区别
如果你想更明确地写,也可以把>>展开成>>=的形式,效果是一样的:
rev2lines = getLine >>= (\line1 -> getLine >>= (\line2 -> putStrLn (reverse line2) >>= (\_ -> putStrLn (reverse line1)) ) )
这里\_表示我们忽略第一个putStrLn的返回值(因为它是(),本来也没用),直接执行第二个打印动作。不过用>>会更简洁,因为它就是为这种场景设计的。
总结一下转换的核心思路:把do块里的每一行,都嵌套到前一行绑定后的lambda中,确保所有后续操作都能访问到前面绑定的变量。
内容的提问来源于stack exchange,提问作者Swapnil B.
相关产品推荐
相关产品推荐

