зачем писать объявления типов в Haskell? - PullRequest
24 голосов
/ 06 июня 2011

Я новичок в Haskell и пытаюсь понять, почему нужно писать объявления типов.Поскольку у Haskell есть вывод типа, когда мне вообще нужна первая строка?Кажется, что GHCI генерирует правильный вывод с использованием ': t'

Единственный пример, который я нашел до сих пор, который, кажется, нуждается в объявлении, следующий:

maximum' :: (Ord a) => [a] -> a  
maximum' = foldr1 max

Однако, если я добавлюОбъявление флага "-XNoMonomorphismRestriction" больше не требуется.Существуют ли конкретные ситуации, когда вывод типов не работает, и нужно указывать типы?

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

РЕДАКТИРОВАТЬ: Оказывается, что вывод типа является обоюдоострым мечом раздел книги Real World Haskell содержит хорошее обсуждение этой темы.

Ответы [ 5 ]

28 голосов
/ 06 июня 2011
  • когда у вас большие программы на Haskell, наличие сигнатур типов часто дает вам лучшие сообщения об ошибках от компилятора
  • , иногда вы можете узнать, что функция делает из ее имени и ее сигнатуры
  • часто функция гораздо понятнее с сигнатурами типов, например, если вы используете карри
  • , даже при написании программ становится легче, я часто начинаю с сигнатур типов и большинства функций, объявленных как undefined.Это все компилирует, я знаю, что моя идея, кажется, подходит не так уж плохо.Затем я продолжаю и заменяю undefined реальным кодом
15 голосов
/ 06 июня 2011

Рассмотрим read "5".Как Haskell может знать тип read "5"?Это невозможно, поскольку нет способа разрешить результат операции, поскольку read определяется как (Read a) => String -> a.a не зависит от строки, поэтому он должен использовать контекст.

Однако обычно контекст - это что-то вроде Ord или Num, поэтому его невозможно определить.Это не ограничение мономорфизма, а еще один случай, который никогда не может быть обработан должным образом.

Примеры:

Не работает:

read "0.5"
putStrLn . show . read $ "0.5"

Работает:

read "0.5" :: Float
putStrLn . show . (read :: String -> Float) $ "0.5"

Это необходимо, потому что экземпляр Show по умолчанию, если я правильно помню, это Int.

5 голосов
/ 07 июня 2011

Душевное спокойствие . Иногда приятно убедиться, что компилятор согласен с вашим восприятием типа функции. Если выведенный тип не объединяется с данным типом, компилятор будет кричать на вас. Когда вы ознакомитесь с системой типов, вы обнаружите, что дополнительные сигнатуры типов могут быть большим благом для вашей уверенности в кодировании.

4 голосов
/ 06 июня 2011

Обычно это потому, что легче читать, а иногда и писать. В строго типизированном языке, таком как Haskell, часто вы обнаруживаете, что создаете функции, которые берут некоторые типы и выводят другой тип, и вы полагаетесь на то, что представляют собой эти типы вместо их имен. После того, как вы привыкнете к тому, как работает система типов, она может прояснить, что вы собираетесь делать, и компилятор может поймать вас, если вы сделали что-то не так.

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

Тогда бывают ситуации, когда вывод типа не работает, например, пример "read", который дал другой ответ. Но это скорее встроенные определения типов, а не определения типов для функции.

3 голосов
/ 18 июня 2011

Одна важная вещь, которую я на самом деле не охватывал ни в одном ответе, - это то, что вы часто пишете определения типов и подписи типов, прежде чем записывать какой-либо реальный код.Как только вы закончите эту «спецификацию», ваша реализация будет проверена на соответствие ей при ее написании, что облегчит обнаружение ошибок раньше, когда компилятор проверит соответствие ваших типов.Например, если вы знаете, что у чего-то должна быть подпись Int -> Int -> [a] -> [a], но при ее написании вместо создания экземпляров двух параметров x и y вы создаете экземпляр только одного параметра x случайно и используете его дважды,компилятор поймает ошибку в точке, где вы определили функцию, а не в той точке, в которой вы пытались использовать ее так, как предполагалось.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...