Следует ли указывать сигнатуру типа для main или нет? Почему, почему нет?

Из главы 9 книги Изучите Haskell во благо я узнал, что

По соглашению мы обычно не указываем объявление типа для 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 в любом случае?


person jub0bs    schedule 09.04.2015    source источник
comment
Отсутствие подписи типа верхнего уровня должно быть ошибкой. На самом деле это должно быть незаконно.   -  person Shoe    schedule 09.04.2015
comment
Незаконное, как в должно быть наказуемо как преступление. Не является незаконным на языке.   -  person Shoe    schedule 09.04.2015
comment
В частности, сигнатура типа exported верхнего уровня может быть ошибкой. Я использую много очень общих помощников, которые не экспортируются, не имеют общедоступных документов, а их подписи длиннее, чем их тела. Выписывание их совсем не помогает, а просто раздражает.   -  person Bartek Banachewicz    schedule 09.04.2015
comment
В книге просто говорится, что они не собираются предоставлять аннотации типов для main, чтобы уменьшить беспорядок в их представлении. Они не подразумевают, что вы не должны комментировать это.   -  person chi    schedule 09.04.2015
comment
@chi Интересно; Я не так прочитал. Однако я не уверен, как следует интерпретировать это предложение. Еще один пример двусмысленного мы.   -  person jub0bs    schedule 09.04.2015
comment
У меня также складывается впечатление, что в предложении речь идет о соглашении, используемом в книге, а не о сообществе Haskell в целом. Я лично обнаружил, что предоставление main явной сигнатуры типа очень полезно на практике.   -  person shang    schedule 09.04.2015


Ответы (2)


Ну, вообще говоря, как становится ясно из этого предупреждения, всегда рекомендуется давать привязкам верхнего уровня сигнатуру типа. На самом деле правильнее было бы сказать

По соглашению мы указываем объявление типа для всего1.

Конечно, в большом проекте main сама по себе требует незначительных усилий, поэтому нет никакого смысла опускать подпись. Просто напишите это, для последовательности.

Однако, хотя Haskell отлично подходит для правильно структурированных проектов и на самом деле существует тенденция писать почти все в библиотеках, он также на удивление хорош как язык быстрых сценариев для вещей, которые другие люди написали бы на Python или Perl. И в этих случаях вы, как правило, не особо заботитесь о безопасности, хорошей документации и т. д., вы просто хотите быстро записать что-то как можно более краткое, что делает работу. Вы также обычно не компилируете эти сценарии с помощью -Wall, а просто выполняете их с помощью runhaskell. И поскольку скрипты всегда должны содержать main (в отличие от большинства других исходных файлов Haskell), действительно достаточно разумно опустить здесь подпись.

Я все еще подозреваю, что большинство Haskellers в настоящее время делают пишут main::IO() даже в самых простых сценариях, если только по привычке.


1Только все на верхнем уровне, т.е. Локальные подписи иногда тоже имеют смысл, но часто они загромождают код.

person leftaroundabout    schedule 09.04.2015
comment
Спасибо за разъяснения. - person jub0bs; 09.04.2015
comment
Также для больших проектов имеет смысл включить -fwarn-missing-signatures и -Werror, что затем заставляет main иметь сигнатуру типа. - person Petr; 10.04.2015
comment
@PetrPudlák Обычно я использую -Wall (который, я думаю, включает -fwarn-missing-signatures) в сочетании с -Werror. - person jub0bs; 10.04.2015

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

person Reid Barton    schedule 09.04.2015
comment
Интересный. Не могли бы вы добавить ссылку на то, где в стандарте вы читали, что main просто должен иметь какой-то тип формы IO a? - person jub0bs; 09.04.2015
comment
@Jubobs: см. второй абзац haskell.org/onlinereport/haskell2010/ haskellch5.html#x11-980005 - person Reid Barton; 09.04.2015
comment
Я бы сказал, что отсутствие требования main :: IO () является (очень) незначительным недостатком стандарта. Впрочем, это дело личного вкуса. - person chi; 09.04.2015
comment
Не могли бы вы добавить такой пример, когда main заканчивается на IO (IO a)? - person Petr; 10.04.2015
comment
@PetrPudlák Надуманный пример: main = return getLine, который имеет тип IO (IO String). - person jub0bs; 10.04.2015
comment
Или чуть менее надуманный, main = putStrLn <$> getLine. - person Reid Barton; 10.04.2015