Когда использовать вывод типа в Haskell? - PullRequest
15 голосов
/ 21 января 2009

Мне любопытно, как часто опытные программисты на Haskell действительно используют вывод типов на практике. Я часто вижу, что его хвалят как преимущество перед всегда явными объявлениями, необходимыми в некоторых других языках, но по какой-то причине (возможно, только потому, что я новичок), он "чувствует" право писать сигнатуру типа почти постоянно и я уверен, что в некоторых случаях это действительно необходимо.

Могут ли некоторые опытные Хаскелеры (Haskellites? Haskellizer?) Дать какой-нибудь вклад?

Ответы [ 4 ]

16 голосов
/ 21 января 2009

Это все еще преимущество, даже если вы пишете сигнатуры типов, потому что компилятор будет отлавливать ошибки типов в ваших функциях. Я обычно тоже пишу сигнатуры типов, но опускаю их в таких местах, как where или let, где вы фактически определяете новые символы, но не чувствуете необходимость указывать сигнатуру типа.

Глупый пример со странным способом вычисления квадратов чисел:

squares :: [Int]
squares = sums 0 odds
  where
    odds = filter odd [1..]
    sums s (a:as) = s : sums (s+a) as

square :: Int -> Int
square n = squares !! n

odds и sums - это функции, которым потребуется подпись типа, если компилятор не выведет их автоматически.

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

squares :: [a]
squares = ...

Компилятор может сделать вывод, что это недопустимо, поскольку для одной из используемых функций (функция odd из стандартной библиотеки) необходимо a в классе типов Integral. На других языках вы обычно узнаете это только позже.

Если вы напишите это как шаблон в C ++, вы получите ошибку компилятора при использовании функции для нецелого типа, но не при определении шаблона. Это может быть довольно запутанным, потому что не сразу понятно, где вы ошиблись, и вам, возможно, придется просмотреть длинную цепочку сообщений об ошибках, чтобы найти реальный источник проблемы. И в чем-то вроде Python вы получаете ошибку во время выполнения в какой-то неожиданный момент, потому что у чего-то не было ожидаемых функций-членов. А в еще более слабо типизированных языках вы можете не получить никаких ошибок, а только неожиданные результаты.

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

5 голосов
/ 21 января 2009

Я всегда пишу сигнатуру типа для функций и значений верхнего уровня, но не для вещей в предложениях "где", "let" или "do".

Во-первых, функции верхнего уровня обычно экспортируются, и Хэддоку требуется объявление типа для генерации документации.

Во-вторых, когда вы делаете ошибку, ошибки компилятора намного легче декодировать, если у компилятора есть информация о типе. На самом деле, иногда в сложном предложении «где» я получаю непонятную ошибку типа, поэтому я добавляю временные объявления типов, чтобы найти проблему, немного похоже на эквивалент уровня printf для отладки.

Поэтому, чтобы ответить на исходный вопрос, я часто использую вывод типов, но не 100% времени.

4 голосов
/ 22 января 2009

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

Как и другие, я почти всегда ставлю сигнатуру типа для функции верхнего уровня и почти никогда не ставлю никаких других объявлений.

Другой вывод типа места неоценим в интерактивном цикле (например, с GHCi). Этот метод наиболее полезен, когда я проектирую и отлаживаю какую-то необычную новую функцию более высокого порядка или что-то подобное.

1 голос
/ 25 декабря 2009

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

Другое использование - это когда вы создаете «внутреннюю функцию» внутри функции верхнего уровня, но вы не знаете, как создать внутреннюю функцию или даже какой она должна быть. Что вы можете сделать, это передать внутреннюю функцию в качестве аргумента функции верхнего уровня, а затем спросить ghci о типе функции уровня типа. Это будет включать в себя тип внутренней функции. Затем вы можете использовать такой инструмент, как Hoogle, чтобы проверить, существует ли эта функция в библиотеке.

...