问题与Haskell的“做”

时间:2009-10-20 08:43:27

标签: haskell syntax monads

我编写了一个Haskell函数,用于计算给定列表中每个数字的阶乘,并将其打印到屏幕上。

factPrint list =
if null list
    then putStrLn ""
    else do putStrLn ((show.fact.head) list)
        factPrint (tail list)

该功能有效,但我发现第三行有点令人困惑。 为什么编译器(GHC)没有报告错误,因为在“putStrLn”(准?)函数之前没有“do”? 如果我从第4行省略“do”,则会按预期弹出错误。

我对Haskell及其方式都很陌生,所以如果我说的话过于愚蠢,请原谅我。

4 个答案:

答案 0 :(得分:11)

do putStrLn ((show.fact.head) list)
   factPrint (tail list)

实际上是另一种写作方式

putStrLn ((show.fact.head) list) >> factPrint (tail list)

反过来意味着

putStrLn ((show.fact.head) list) >>= \_ -> factPrint (tail list)

do符号是将这些monad串在一起的便捷方式,没有其他丑陋的语法。

如果do中只有一个声明,那么你就不会将任何内容串起来,do是多余的。

答案 1 :(得分:5)

如果您是Haskell的新手,请将do视为类似C语言if之后的必需大括号:

if (condition)
  printf("a"); // braces not required
else {
  printf("b"); // braces required
  finish();
}

do在Haskell中的工作方式相同。


也许有助于查看factPrint的类型,然后重构使用模式匹配:

factPrint :: [Int] -> IO ()
factPrint [] = putStrLn ""
factPrint list = do
  putStrLn (show.fact.head) list
  factPrint (tail list)

因此,如果factPrint返回IO (),而putStrLn ""的类型为IO (),则factPrint []等于putStrLn ""是完全合法的。不需要do - 事实上,如果您不想要跟踪换行符,则可以说factPrint [] = return ()

答案 2 :(得分:4)

do用于将多个monadic表达式绑定在一起。只有一个表达式后,它没有任何效果。

对于if格式良好,只需要then子句和else子句具有相同的类型。由于这两个子句的类型都为IO (),因此就是这种情况。

答案 3 :(得分:3)

do关键字用于排序,如果每个分支都是单个语句,则Haskell中的if-then-else根本不需要包含do,例如。

if a
  then b
  else c

在您的示例中需要do,因为您在其他分支上对两个操作进行了排序。如果省略do,那么factPrint(tail list)语句被认为不属于该函数,因此编译器会因为遇到意外语句而抱怨。