为什么 Haskell 中的 main 函数没有任何参数?

Mod*_*i57 15 haskell functional-programming

我对 Haskell 有点陌生。我已经完成了一两个教程,但我没有太多经验。

Haskell 中的每个函数都是纯函数,这就是为什么没有 IO-monad 就不能有任何 I/O。我不明白的是,为什么程序参数也必须是 IO 操作?参数像传递函数一样传递给程序。为什么不能像在函数中那样访问参数?

为了说清楚,我不明白,为什么主要功能必须是这样的

main :: IO()
main = do
    args <- getArgs
    print args
Run Code Online (Sandbox Code Playgroud)

而不是这个

main :: [String] -> IO()
main args = do
    print args
Run Code Online (Sandbox Code Playgroud)

我看不出任何原因,而且我还没有在谷歌上找到答案。

chi*_*chi 16

这是一种语言设计选择。严格来说,这两种方法都不比另一种方法好。

Haskell 可以被设计为具有main任何一种。

当确实需要程序参数时,将它们作为函数参数传递给main.

当不需要程序参数时,将它们传递给main有点麻烦,因为我们需要编写一个更长的类型,以及一个额外的_来丢弃[String]参数。

此外,getArgs让人们访问程序中任何地方(内部IO)的程序参数,而将它们传递给main, only 可能不太方便,因为人们将被迫在程序中传递它们,这可能很不方便。

(简短的题外话)不管怎样,很久以前我和你有类似的反应,当时我发现在 Java 中我们有void main()而不是int main()在 C 中。然后我意识到在大多数程序中我总是写return 0;在最后,所以它总是要求这样做毫无意义。在 Java 中,这是隐式默认值,当我们确实需要返回其他内容时,我们使用System.exit(). 即使这是在以前的语言(在这种情况下为 C)中完成的方式,新的语言也可以选择一种新的方式来提供相同的功能。

  • Java 程序不一定会在 main 返回时退出,因此将进程退出代码与 main 方法返回值联系起来是没有意义的。 (5认同)

Ben*_*Ben 12

我倾向于同意 chi 的回答,没有明显令人信服的理由必须这样做,所以这实际上归结为很久以前一小群人做出的主观判断。不能保证背后会有任何特别令人满意的理由。

这是我想到的一些推理(这可能是也可能不是原始设计师当时的想法,甚至可能会同意)。

我们真正喜欢做的事情是这样的:

main :: Integer -> String -> Set Flag -> IO ()
Run Code Online (Sandbox Code Playgroud)

(对于一些将整数、字符串和一组标志作为命令行参数的假设程序)

能够编写小的命令行程序就好像它们只是命令行参数的函数一样会很棒!但这需要操作系统(或至少是外壳程序)了解 Haskell 程序中使用的类型并知道如何解析它们(以及如果解析失败或没有足够的参数等该怎么办) ,这不会发生。

也许我们可以编写一个包装器来做到这一点。它可以负责将原始字符串命令行参数解析为 Haskell 类型并生成错误消息(如果需要),然后调用main我们。但是等等,我们完全可以做到!我们只需要调用包装器main(并重命名我们之前调用的内容main)!

关键是:如果你想把你的程序看作是一个简单的外部输入函数,这很有意义,但main不是那个函数main作为一个包装器工作得更好,它处理通过无类型接口接收输入和调用“真正是”你的程序的函数的丑陋细节。

强制您getArgs在设置代码中包含对 的调用使处理命令行参数的工作变得更加明显,而不仅仅是访问它们,并且可能会促使您编写一些额外的处理代码,而不仅仅是编写main (arg1 : arg2 : _) = do stuffWith arg1 arg2.

此外,将我们拥有的界面转换为您想要的界面非常简单:

import System.Environment

main = real_main =<< getArgs

real_main :: [String] -> IO ()
real_main args = print args
Run Code Online (Sandbox Code Playgroud)

所以你可以随心所欲地拥有它!