是否应该为 main 指定类型签名?为什么/为什么不?

Should one specify a type signature for main or not? Why / why not?

我从 chapter 9 of Learn You A Haskell For Great Good 那里了解到

By convention, we don't usually specify a type declaration for main.

据我所知,这种约定很普遍。但是,如果我使用 -Wall 标志编译缺少 main 类型签名的程序,例如

-- test.hs

-- main :: IO ()
main = print (1 :: Int)

GHC 确实发出警告:

$ ghc -Wall test.hs
[1 of 1] Compiling Main             ( test.hs, test.o )

test.hs:2:1: Warning:
    Top-level binding with no type signature: main :: IO ()
Linking test ...
$

我很困惑...如果 main 的类型签名确实是多余的,为什么 -Wall 会导致 GHC 在丢失时抱怨?无论如何指定 main 的类型是否有充分的理由(除了消除该警告之外)?

嗯,一般来说,正如该警告所表明的那样,总是给顶级绑定一个类型签名是个好主意。其实说

更合理

By convention, we do specify a type declaration for everything1.

当然,在一个大项目中,main本身就是一个微不足道的努力,所以省略签名真的没有任何意义。为了保持一致性,就写出来吧。

然而,尽管 Haskell 非常适合结构合理的项目,而且实际上有一种趋势是在 中编写几乎所有内容,但它作为一种快速脚本语言也出奇地好,对于其他人会用 Python 或 Perl 编写的东西。在那些情况下,您通常不太关心安全性和良好的文档等,您只想快速写下尽可能简洁的内容来完成工作。您通常也不使用 -Wall 编译这些脚本,而只是使用 runhaskell 执行它们。由于脚本总是需要包含 main(与大多数其他 Haskell 源文件不同),省略此处的签名确实足够明智。

我仍然怀疑现在大多数 Haskell 用户 main::IO() 即使是在最简单的脚本中,如果只是出于习惯的话。


1只有顶层的所有东西,也就是说。本地签名有时也很有意义,但通常它们会使代码变得混乱。

main 编写类型签名实际上是一个非常好的主意,否则如果你太喜欢尝试以无点形式编写内容,你可能会得到 main类型 IO (IO ())。这是被接受的(语言标准说 main 只需要有某种形式的 IO a)但是作为 main 结果的 "inner IO action" 将被丢弃,这几乎肯定不是你想要的(你可能想要 join 它)。