Haskell 理解I/O单子

Haskell 理解I/O单子,haskell,functional-programming,Haskell,Functional Programming,我仍在与Haskell抗争,现在我遇到了一个问题,就是如何将注意力集中在输入/输出monad上: main=do 线串 reverseWords=unwords。地图倒过来。话 我理解,因为像Haskell这样的函数式语言不能基于函数的副作用,所以必须发明一些解决方案。在这种情况下,似乎所有内容都必须包装在do块中。我有简单的例子,但在这种情况下,我真的需要有人解释 为什么仅仅使用一个do块来执行I/O操作还不够?为什么在if/else的情况下你必须打开一个全新的?另外,domonad的“sc

我仍在与Haskell抗争,现在我遇到了一个问题,就是如何将注意力集中在输入/输出monad上:

main=do
线串
reverseWords=unwords。地图倒过来。话
我理解,因为像Haskell这样的函数式语言不能基于函数的副作用,所以必须发明一些解决方案。在这种情况下,似乎所有内容都必须包装在
do
块中。我有简单的例子,但在这种情况下,我真的需要有人解释


为什么仅仅使用一个
do
块来执行I/O操作还不够?为什么在if/else的情况下你必须打开一个全新的?另外,
do
monad的“scope”在什么时候结束,也就是说,什么时候可以只使用标准的Haskell术语/函数?

do块涉及与第一条语句相同缩进级别的任何内容。因此,在您的示例中,它实际上只是将两件事情连接在一起:

 line <- getLine
但是无论多么复杂,
do
语法都不会研究这些表达式。因此,所有这些都与

main :: IO ()
main = do
   line <- getLine
   recurseMain line

现在,显然,
recurseMain
中的东西无法知道函数是在main的
do
块中调用的,因此您需要使用另一个
do
do
实际上什么都不做,它只是方便组合语句的语法糖。一个可疑的类比是将
do
[]
进行比较:

如果有多个表达式,可以使用

(1 + 2) : (3 * 4) : (5 - 6) : ...
但是,这很烦人,因此我们可以使用
[]
表示法,它编译成相同的东西:

[1+2, 3*4, 5-6, ...] 
do
  putStrLn "What's your name?"
  name <- getLine
  putStrLn $ "Hi " ++ name
类似地,如果您有多个IO状态,您可以:

但是,这很烦人,因此我们可以使用
do
表示法,它编译成相同的东西:

[1+2, 3*4, 5-6, ...] 
do
  putStrLn "What's your name?"
  name <- getLine
  putStrLn $ "Hi " ++ name
do
你叫什么名字

name
do
只是一种语法上的糖分,可以避免用
>=
>
编写过于复杂的语句。它适用于所有单子,与IO没有特殊的关联。您不必在示例代码中使用第二个
do
(您甚至不需要第一个
do
,但生成的代码将更难阅读),您也可以编写
else-putStrLn(reverseWords-line)>>main
,它将具有相同的效果。我在这里回答了一个基本类似的问题:这有帮助吗?诀窍是从根本上看所有函数的返回类型,然后一切都会就绪。有更多的解释。如果你在与单子作斗争,博客文章是一个很好的阅读工具,这似乎是事实。在打开.hs文件和ghci提示的情况下阅读,这样您就可以做练习了。非常感谢您的帮助,好的教程可以向我更好地解释一切,而不仅仅是理论。
(putStrLn "What's your name?") >> getLine >>= (\name -> putStrLn $ "Hi " ++ name)
do
  putStrLn "What's your name?"
  name <- getLine
  putStrLn $ "Hi " ++ name