了解I/O monad

Nie*_*and 6 haskell functional-programming

我仍在努力与Haskell,现在我遇到了一个问题,从这个例子包围我的思想输入/输出monad :

main = do   
line <- getLine  
if null line  
    then return ()  
    else do  
        putStrLn $ reverseWords line  
        main  

reverseWords :: String -> String  
reverseWords = unwords . map reverse . words
Run Code Online (Sandbox Code Playgroud)

我理解,因为像Haskell这样的函数语言不能基于函数的副作用,所以必须发明一些解决方案.在这种情况下,似乎所有东西都必须包裹在一个do块中.我得到了简单的例子,但在这种情况下我真的需要一些解释.

为什么do在I/O操作中使用一个单独的块是不够的?为什么你必须在if/else情况下打开全新的?此外,什么时候,我不知道如何调用它,domonad的"范围" 结束,即你什么时候才能使用标准的Haskell术语/函数?

lef*_*out 8

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

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

而所有其余的,恰好相当大:

 if null line  
  then return ()
  else do
      putStrLn $ reverseWords line  
      main  
Run Code Online (Sandbox Code Playgroud)

但无论多么复杂的do语法看起来并不进入这些表达式.所以这一切都完全一样

main :: IO ()
main = do
   line <- getLine
   recurseMain line
Run Code Online (Sandbox Code Playgroud)

有辅助功能

recurseMain :: String -> IO ()
recurseMain line
   | null line  = return ()
   | otherwise  = do
           putStrLn $ reverseWords line
           main
Run Code Online (Sandbox Code Playgroud)

现在,显然这些东西recurseMain不知道函数是在domain的一个块内调用的,所以你需要使用另一个do.


tha*_*guy 8

do实际上并没有做任何事情,它只是简单组合语句的语法糖.一个可疑的比喻是比较do[]:

如果您有多个表达式,可以使用:以下方法将它们组合成列表

(1 + 2) : (3 * 4) : (5 - 6) : ...
Run Code Online (Sandbox Code Playgroud)

但是,这很烦人,所以我们可以使用[]符号来编译同样的东西:

[1+2, 3*4, 5-6, ...] 
Run Code Online (Sandbox Code Playgroud)

同样,如果您有多个IO语句,则可以使用>>和组合它们>>=:

(putStrLn "What's your name?") >> getLine >>= (\name -> putStrLn $ "Hi " ++ name)
Run Code Online (Sandbox Code Playgroud)

但是,这很烦人,所以我们可以使用do符号来编译同样的东西:

do
  putStrLn "What's your name?"
  name <- getLine
  putStrLn $ "Hi " ++ name
Run Code Online (Sandbox Code Playgroud)

现在你需要多个do块的答案很简单:

如果您有多个值列表,则需要多个[]s(即使它们是嵌套的).

如果你有多个monadic语句序列,你需要多个dos(即使它们是嵌套的).