Haskell - 'main'有什么独特之处?

7
使用以下代码:
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

6个回答

22
因为语言规范规定了这样做。
一个Haskell程序由多个模块组成,其中一个惯例上必须称为Main并且必须导出值main。程序的值是模块Main中标识符main的值,它必须是某种类型t的计算IO t(请参见第7章)。当程序被执行时,执行计算main,并且其结果(类型为t)将被丢弃。

5
正如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视为特殊函数。

1

根据定义,main是一个不带参数并返回类型为IO a的值,该值由运行时丢弃的函数。您的错误消息表明,您的main不符合这些要求。这确实是真的,因为您的main接收了两个参数。

为了访问命令行参数,请使用System.Environment.getArgs


1
“Main.main”有什么独特之处?
现在Haskell拥有FFI,这在比喻上是简单的:
这段代码:
module Main(main) where main :: IO () ⋮
几乎等同于:
module Main() where foreign export "Hmain" main:: IO () ⋮
(...“比喻”而非“字面上”,让它实际工作留给读者自己练习;-))
那么让Main.main独特的是它默认情况下在Haskell之外可见,允许运行时系统调用您的Haskell程序。对于任何其他Haskell定义,您都需要使用FFI。
它为什么必须是 IO t 类型?
因为在 Haskell 中,只有类型为 IO ... 的值(通常称为“操作”)才能具有可见的效果,例如写入文件。此外,运行时系统只能在 Main.main 是一个 IO 操作时正确地调用它。

0

我知道这个问题很古老,但是我想提一下,你可以在一个没有(明确或隐含地)命名为Main的模块中将任何东西命名为main

module Foo where
main :: Int -> Bool
main = (>3)

代码会编译成功。但是如果你省略了模块头,或者使用 module Main where ...,那么编译会失败。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接