为什么" Main.main"," IO()"的类型而不是" IO a"?

时间:2014-03-17 15:13:26

标签: haskell

为什么Main.main的类型必须是IO ()而不是IO StringIO IntIO whatever

main :: IO ([] Char) -- Type error
main = (>>) ((>>=) getLine putStrLn) getLine

3 个答案:

答案 0 :(得分:5)

如果您将IO a视为" IO计算产生a"然后它引出了一个问题,即运行时对a生成的main的影响。它可以放弃它。这需要像

这样的功能
void :: IO a -> IO ()   -- defined more generally in Control.Monad

然后给我们

realMain :: IO ()
realMain = void main

除了文档之外,这没有特别的问题。如果我问你IO a的价值,那么我可能会选择用a做些什么。如果我要求IO ()的价值,那么这种不确定性就会减少:我可以随时创造()我自己的价值,我不需要你,因此我只关心这一方面运行IO计算的效果。

这更好地匹配main的实际用法,因此为了更好的类型清晰度,Haskell运行时可能会要求main :: IO ()

这也迫使main的创建者明确表达他们对任何"返回值"他们创造。如果我有一个mainish :: IO a,那么在将其交给运行时之前我需要明确地void


所有人都说,正如Sibi指出的那样,GHC实际接受main :: IO a并默默地丢弃a,所以这一点有点没有实际意义。请参阅Haskell报告的Chapter 5简介。

答案 1 :(得分:4)

main的类型可以是IO a。这个样子:

main :: IO Int
main = do
  putStrLn "test"
  return 2

然后你可以自己执行:

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

main是程序开始执行的功能。因此,类型为main的{​​{1}}函数没有多大意义,因为其他函数实际上不会使用IO a中包含的a

答案 2 :(得分:1)

实际上,main可以是某个类型IO T T不是()

我认为这不是一个错误,但这不是一个错误。 main没有理由成为其他IO ()类型的任何类型,它可能会导致非常微妙的运行时问题,例如

main = putStrLn <$> getContents

我遇到了一个问题需要一段时间来弄明白。当然,这可以通过始终为所有顶级定义(每个人都应该这样做)提供类型签名来防止,但我仍然觉得没有理由允许除main之外的任何IO类型。 IO ()

(上述代码的问题是main实际上评估的是包含 IO操作的IO操作,而不是实际上是IO操作。即,类型为{{ 1}}。正确的代码可以是main :: IO (IO ())main = join $ putStrLn <$> getContents)。