Подтвердить что ты не робот

Зачем писать объявления типа в Haskell?

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

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

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

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

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

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

4b9b3361

Ответ 1

Рассмотрим 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.

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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