Почему тип "Main.main", "IO ()", а не "IO a"?

Почему тип Main.main должен быть IO () и не IO String или же IO Int или же IO whatever?

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

3 ответа

Решение

Если вы читаете 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 это перед передачей его во время выполнения.


Тем не менее, как указывает Сиби, GHC фактически принимает main :: IO a и молча отбрасывает aТаким образом, вопрос немного спорный. Смотрите вступление к Главе 5 Отчета на Haskell.

Тип 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 функция с типом IO a не имеет особого смысла, потому что никакая другая функция не будет фактически использовать a завернут в IO,

Вы на самом деле можете иметь main быть какой-то тип IO T где T не (),

Я думаю, это плохо, что это не ошибка, хотя. Там нет причин для main быть любым другим типом IO () и это может привести к очень тонким проблемам во время выполнения, как

main = putStrLn <$> getContents

У меня была такая проблема, мне потребовалось время, чтобы разобраться. Конечно, этого можно избежать, всегда предоставляя сигнатуру типа для всех определений верхнего уровня (что должен делать каждый), но я все еще чувствую, что нет причины разрешать какой-либо тип ввода-вывода для main Кроме как IO (),

(Проблема с приведенным выше кодом заключается в том, что main фактически оценивается как действие ввода-вывода, содержащее действие ввода-вывода, а не фактически является действием ввода-вывода. То есть тип main :: IO (IO ()), Правильный код будет либо main = join $ putStrLn <$> getContents или же main = putStrLn =<< getContents).

Другие вопросы по тегам