【问题标题】:Understanding I/O monad and the use of "do" notation理解 I/O monad 和“do”符号的使用
【发布时间】:2014-08-09 05:40:59
【问题描述】:

我仍在为 Haskell 苦苦挣扎,现在我遇到了一个问题,就是围绕 Input/Output monad from this example:

main = do   
line <- getLine  
if null line  
    then return ()  
    else do  
        putStrLn $ reverseWords line  
        main  
  
reverseWords :: String -> String  
reverseWords = unwords . map reverse . words

我知道因为像 Haskell 这样的函数式语言不能基于函数的副作用,所以必须发明一些解决方案。在这种情况下,似乎所有内容都必须包装在 do 块中。我得到了简单的例子,但在这种情况下,我真的需要有人解释:

  1. 为什么对于 I/O 操作仅使用一个 do 块还不够?
  2. 为什么必须在 if/else 情况下打开全新的?
  3. 另外,我不知道怎么称呼它 do monad 的“范围”什么时候结束,即什么时候可以使用标准的 Haskell 术语/函数?

【问题讨论】:

  • do 只是语法糖,以避免使用 &gt;&gt;=&gt;&gt; 编写过于复杂的语句,它适用于所有 monad,并且与 IO 没有特殊关联。您不必在示例代码中使用第二个do(您甚至不需要第一个do,但生成的代码会更难阅读),您也可以编写else putStrLn (reverseWords line) &gt;&gt; main,它会有同样的效果。
  • 我在这里回答了一个非常相似的问题:stackoverflow.com/questions/23049409/… 这有帮助吗?诀窍是基本上看所有函数的返回类型,一切都会到位。
  • This 博客文章是一个很好的读物,如果你在一般情况下都在与 monad 作斗争,情况似乎就是这样。阅读 .hs 文件并打开 ghci 提示符,以便您进行练习。
  • 非常感谢您的帮助,好的教程可以比理论更好地解释一切。

标签: haskell functional-programming do-notation


【解决方案1】:

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

 line <- getLine

以及所有其他的,恰好更大:

 if null line  
  then return ()
  else do
      putStrLn $ reverseWords line  
      main  

但无论多么复杂,do 语法都不会进入这些表达式。所以这一切都和

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

使用辅助函数

recurseMain :: String -> IO ()
recurseMain line
   | null line  = return ()
   | otherwise  = do
           putStrLn $ reverseWords line
           main

现在,显然recurseMain 中的内容无法知道该函数是在 main 的 do 块中调用的,因此您需要使用另一个 do

【讨论】:

    【解决方案2】:

    do 实际上并没有做任何事情,它只是用于轻松组合语句的语法糖。一个可疑的类比是将do[] 进行比较:

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

    (1 + 2) : (3 * 4) : (5 - 6) : ...
    

    但是,这很烦人,所以我们可以改用 [] 表示法,它编译成同样的东西:

    [1+2, 3*4, 5-6, ...] 
    

    同样,如果你有多个 IO 语句,你 can combine them using &gt;&gt; and &gt;&gt;=:

    (putStrLn "What's your name?") >> getLine >>= (\name -> putStrLn $ "Hi " ++ name)
    

    但是,这很烦人,所以我们可以改用do 表示法,它编译成同样的东西:

    do
      putStrLn "What's your name?"
      name <- getLine
      putStrLn $ "Hi " ++ name
    

    现在,为什么需要多个 do 块的答案很简单:

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

    如果您有多个单子语句序列,则需要多个 dos(即使它们是嵌套的)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-17
      • 2020-06-14
      相关资源
      最近更新 更多