Почему неимоверный полиморфизм разрешен только для функций в Haskell? - PullRequest
8 голосов
/ 04 июня 2019

В Хаскеле я не могу написать

f :: [forall a. a -> a]
f = [id]

потому что

• Illegal polymorphic type: forall a. a -> a
  GHC doesn't yet support impredicative polymorphism

Но я могу с радостью сделать

f :: (forall a. a -> a) -> (a, b) -> (a, b)
f i (x, y) = (i x, i y)

Итак, как я вижу, GHC поддерживает неумышленный полиморфизм, что противоречит сообщению об ошибке выше. Почему конструктор типа (->) специально обрабатывается в этом случае? Что мешает GHC распространить эту функцию на все типы данных?

1 Ответ

12 голосов
/ 04 июня 2019

Полиморфизм высшего ранга является частным случаем неумышленного полиморфизма, где конструктор типа равен (->) вместо любого произвольного конструктора, такого как [].

Основные проблемы с непредсказуемостью состоят в том, что он делает тип проверка hard и ввод логического вывода невозможны - и действительно, мы не можем вывести типы более высокого ранга, чем 2: вы должны предоставить аннотацию типа.Это причина существования расширения Rank2Types отдельно от RankNTypes.Но для ограниченного случая (->) существуют упрощенные алгоритмы для проверки этих типов и выполнения необходимого объема логического вывода для удобства программиста, такие как Полная и простая двунаправленная проверка типов для полиморфизма более высокого ранга - сравните это со сложностью Boxy Types: вывод для типов более высокого ранга и непредсказуемости .

Фактические причины в GHC отчасти исторические: там было расширение ImpredicativeTypes, которое сейчас устарело, но оно никогда не работало должным образом или эргономично.Частично проблема заключалась в том, что у нас еще не было расширения TypeApplications, поэтому не было удобного способа явно указать полиморфный тип в качестве аргумента типа, и компилятор попытался сделать больше вывода, чем следовало бы.Теперь, когда у нас есть TypeApplications, ограниченная форма ImpredicativeTypes может стать возможной в будущем.

Однако это не очень актуально, поскольку в течение некоторого времени были обходные пути: с RankNTypes выможно «скрыть» другие формы неэффективности, обернув полиморфный тип в newtype и явным образом упаковав и распаковав его, чтобы сообщить компилятору, где именно вы хотите обобщить и создать экземпляр переменных типа.

newtype Id = Id { unId :: forall a. a -> a }

f :: [Id]
f = [Id id]  -- generalise

(unId (head f) (), unId (head f) 'x')  -- instantiate to () and Char
...