【问题标题】:Haskell - What makes 'main' unique?Haskell - 是什么让“主要”与众不同?
【发布时间】:2011-09-09 15:48:34
【问题描述】:
使用此代码:
main :: FilePath -> FilePath -> IO ()
main wrPath rdPath = do x <- readFile rdPath
writeFile wrPath x
我收到以下错误:
Couldn't match expected type 'IO t0'
with actual type 'FilePath -> FilePath -> IO()
但是当我将“main”的名称更改为其他名称时,文件编译正确。
main 有什么独特之处,为什么它的类型必须是 IO t0?
【问题讨论】:
标签:
haskell
types
io
compiler-errors
main
【解决方案1】:
因为语言规范says so。
Haskell 程序是一组模块,按照惯例,其中一个必须称为Main,并且必须导出值main。程序的值是模块Main 中标识符main 的值,对于某些t 类型,它必须是IO t 类型的计算(参见第7 章)。当程序执行时,计算main被执行,其结果(t类型)被丢弃。
【解决方案2】:
正如 GolezTrol 所说,所有程序都需要知道在调用函数时要开始执行哪个符号。许多脚本语言不需要(或只是不需要)main 例程,因为它们可以将语句放在顶层。 Haskell、C 和许多其他语言的情况并非如此 - 这些语言需要一个起始位置,并且按照惯例,它是 main 函数(根据 Haskell 规范 - 请参阅 Cat 的答案)。
注意 Haskell 的 main 不接受与程序参数相对应的任何参数 - 这些是通过 System.Environment.getArgs 获得的。
【解决方案3】:
与在 C、Java 或 C# 中一样,main 在某些上下文中是一个特殊标识符,指示程序应该从哪里开始。
在 Haskell 中,main 被定义为具有 IO a 类型。你应该给你的函数一个不同的名字,或者如果你真的希望它成为起点,改变它的签名并让它从命令行读取参数getArgs
虽然你没有特别问,main 也很特别,因为它是 Haskell 程序中唯一可以(安全地)调用 IO 操作的函数。 Haskell 运行时将main 视为特殊的。
【解决方案4】:
根据定义,main 是一个不接受参数并返回一个类型为 IO a 的值的函数,该值被运行时丢弃。您的错误消息是说您的 main 不符合这些要求。确实如此,因为您的main 接收两个参数。
要访问命令行参数,请使用System.Environment.getArgs。
【解决方案5】:
Main.main 有什么独特之处?
既然 Haskell 有一个 FFI,它比喻很简单:
| This code: |
(sort of) |
ends up as: |
module Main(main) where main :: IO () ⋮ |
≈ |
module Main() where foreign export "Hmain" main:: IO () ⋮ |
(...“比喻地”,不是 “字面上” - 把这项工作留作练习;- )
所以Main.main 的独特之处在于它默认在 Haskell 外部可见,允许运行时系统调用您的 Haskell 程序。对于任何其他 Haskell 定义,您都需要使用 FFI。
为什么它的类型必须是IO t?
因为在 Haskell 中,只有 IO ... 类型的值(通常称为 actions)可以产生可见的效果,例如写入文件。此外,运行时系统只有在IO动作时才能正确调用Main.main。
【解决方案6】:
我知道这个问题很古老,但我想我应该提一下,你可以在一个不(显式或隐式)命名为 Main 的模块中命名任何你想要的东西 main。所以
module Foo where
main :: Int -> Bool
main = (>3)
会编译得很好。但是如果你省略了模块头,或者使用module Main where ...,那就不会了。