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

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

Помогает ли компилятор оптимизировать работу или просто избыточная работа для добавления дополнительных подписей типа? Например, часто можно увидеть:

foo :: a -> b
foo x = bar x
      where bar x = undefined

Вместо

foo :: a -> b
foo x = bar x
      where bar :: a -> b
            bar x = undefined

Если я опускаю подпись типа верхнего уровня, GHC дает мне предупреждение, поэтому, если я не получу предупреждения, я уверен, что моя программа верна. Но никаких предупреждений не выдаются, если я опускаю подпись в предложении where.

4b9b3361

Ответ 1

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

Кажется довольно распространенным введение where учащимся Haskell с примерами этой формы, поэтому они продолжают думать, что "нормальный стиль" не должен давать объявления типов для локальных определений. По крайней мере, это был мой опыт изучения Хаскелла. С тех пор я обнаружил, что многие из моих функций, которые достаточно сложны для использования блока where, становятся довольно непостижимыми, если я не знаю тип локальных определений, поэтому я пытаюсь ошибаться, чтобы всегда печатать их сейчас; даже если я думаю, что тип очевиден, когда я пишу код, это может быть не так очевидно, когда я читаю его, не посмотрев на него какое-то время. Небольшое усилие для моих пальцев почти всегда перевешивается даже одним или двумя случаями, когда нужно выполнять вывод типа в моей голове!

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

Ответ 2

Существует класс локальных функций, типы которых не могут быть записаны в Haskell (без использования причудливых расширений GHC, то есть). Например:

f :: a -> (a, Int)
f h = g 1
  where g n = (h, n)

Это связано с тем, что в то время как a в сигнатуре типа f полиморфно просматривается извне f, это не так изнутри f. В g это всего лишь некоторый неизвестный тип, но не какой-либо тип, и (стандартный) Haskell не может выразить "тот же тип, что и первый аргумент функции, который он определен в" на языке своего типа.

Ответ 3

Часто объявления where используются для коротких локальных вещей, которые имеют простые типы или типы, которые легко выводятся. В результате нет никакой выгоды для человека или компилятора, чтобы добавить тип.

Если тип является сложным или не может быть выведен, вы можете захотеть добавить этот тип.

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

Ответ 4

Добавление сигнатуры типа может сделать ваш код быстрее. Возьмем, к примеру, следующую программу (Фибоначчи):

result = fib 25 ;
-- fib :: Int -> Int
fib x = if x<2 then 1 else (fib (x-1)) + (fib (x-2))
  • Без аннотации во 2-й строке требуется 0,010 сек. для запуска.
  • При аннотации Int -> Int требуется 0,002 сек.

Это происходит потому, что, если вы ничего не говорите о fib, оно будет набираться как fib :: (Num a, Num a1, Ord a) => a -> a1, а это означает, что во время выполнения дополнительные функции ( "словари" ) должны передаваться между функциями для представления классов Num/Ord.