Haskell 中的主函数是否总是以 main = do 开头?

Does the main-function in Haskell always start with main = do?

在java中我们总是这样写:

public static void main(String[] args){...}

当我们想开始编写程序时。

我的问题是,Haskell是否相同,即:我可以始终确保声明: main = do,当我想为 Haskell?

中的程序编写代码时

例如:

main = do  
    putStrLn "What's your name?"  
    name <- getLine 
    putStrLn ("Hello " ++ name) 

此程序将询问用户"What's your name?" 然后用户输入将存储在名称变量中,并且 "Hello" ++ name 会在程序终止前显示。

简答,我们必须声明一个main =,但一个do.

main 必须是一个 IO monad 类型 (所以 IO a),其中 a 是任意的(因为它被忽略了),如 here:

The use of the name main is important: main is defined to be the entry point of a Haskell program (similar to the main function in C), and must have an IO type, usually IO ().

但你不一定需要do符号。实际上do is syntactical sugar。您的 main 实际上是:

main =
    putStrLn "What's your name?" >> getLine >>= \n -> putStrLn ("Hello " ++ n)

或者更优雅:

main = putStrLn "What's your name?" >> getLine >>= putStrLn . ("Hello " ++)

所以这里我们写了一个没有do符号的main。有关 脱糖 do 表示法的更多信息,请参阅 here

是的,if 你的 do 块中有不止一行,而且 if 你甚至在使用do 表示法。

完整的 do-notation 语法还包括显式分隔符 -- 大括号和分号:

main = do { putStrLn "What's your name?" 
          ; name <- getLine  
          ; putStrLn ("Hello " ++ name) 
          }

对于它们,缩进除了编码风格外没有任何作用(良好的缩进提高可读性;明确的分隔符确保代码的健壮性,消除与 white-space 相关的脆弱性)。所以当你只有一行 IO 代码时,比如

main = do { print "Hello!" }

没有分号,没有注意缩进,花括号和do关键字本身变得多余:

main = print "Hello!" 

所以,不,不总是。但它经常发生,并且代码的统一性对可读性有很大帮助。


do 块转换为单子代码,但您首先可以将此事实视为实现细节。事实上,你应该。您可以在心理上将 do 符号 公理化 视为一种嵌入式语言。而且,反正就是那个。

简化的 do 语法是:

   do {  pattern<sub>1</sub> <- action<sub>1</sub>
      ;  pattern<sub>2</sub> <- action<sub>2</sub>
      .....................
      ;  return (.....)
      }

每个 action<sub>i</sub> 都是 Haskell 类型 M a<sub> 的值i</sub> 对于一些 monad M 和一些结果类型 a<sub>i</sub>。每个 action 产生自己的结果类型 a<sub>i</sub>all action s 必须属于 same monad 类型 M.

每个 pattern<sub>i</sub> 从相应的操作中接收先前“计算”的结果。

通配符_可以用来忽略它。如果是这种情况,_ <- 部分可以完全省略。


“Monad”是一个可怕的、没有信息量的词,但从概念上讲,它实际上只不过是 EDSL。 嵌入式 领域特定语言意味着我们有原生 Haskell 值代表(在这种情况下)I/O 计算。我们用这种语言编写我们的 I/O 程序,它成为原生 Haskell 值,我们可以像在任何其他原生 [=118] 上一样对其进行操作=] value -- 将它们收集在列表中,将它们组合成更复杂的计算描述(程序)等

main 值就是我们的 Haskell 程序计算出的一个这样的值。编译器看到它,并在 运行 时间执行它代表的 I/O 程序。

重点是我们现在可以拥有一个“函数”getCurrentTime(从表面上看,在函数式范例中这是不可能的,因为它必须 return 在不同的调用中产生不同的结果) ,因为它 不是 return 当前时间 -- 它描述的动作 这样做,当 I/O 对其进行编程 描述 是 运行 按 运行 时间系统。

在类型级别上,这反映在这样的值中,这些值不仅具有一些普通的 Haskell 类型 a,而且具有参数化类型 IO a,由 [=30]“标记” =]属于这个特殊的I/O编程世界。

另请参阅: