-
TypeInType -
- Подразумевает:
- С:
-
8.0.1
- Статус:
-
Устаревшая
Расширение
TypeInTypeтеперь устарело: его единственное действие — включениеPolyKinds(и, следовательно,KindSignatures) иDataKinds.
-
PolyKinds -
- Подразумевает:
- С:
-
7.4.1
- Статус:
Разрешает виды полиморфных типов.
В данном разделе описывается система видов GHC, как она представлена в версии 8.0 и более поздних. Система видов, описанная здесь, всегда активна, с расширениями или без них, хотя это консервативное расширение стандартного Haskell. Приведённые выше расширения просто включают синтаксис и корректируют алгоритм вывода, позволяя пользователям использовать дополнительную выразительность системы видов GHC.
6.4.13.1. Обзор полиморфизма видов
Рассмотрим вывод вида для
data App f a = MkApp (f a)
В Haskell 98 выведенный вид для App — (Type -> Type) -> Type ->
Type. Но это слишком специфично, так как другой подходящий вид Haskell 98 для App — ((Type -> Type) -> Type) -> (Type -> Type) -> Type, где вид, назначенный для a — Type -> Type. Действительно, без видов сигнатур (KindSignatures) необходимо использовать фиктивный конструктор, чтобы заставить компилятор Haskell вывести второй вид. С полиморфизмом видов (PolyKinds), GHC выводит вид forall k. (k -> Type) -> k ->
Type для App, который является его наиболее общим видом.
Таким образом, основное преимущество полиморфизма видов заключается в том, что теперь мы можем вывести эти наиболее общие виды и использовать App в различных видах:
App Maybe Int -- `k` is instantiated to Type data T a = MkT (a Int) -- `a` is inferred to have kind (Type -> Type) App T Maybe -- `k` is instantiated to (Type -> Type)
6.4.13.2. Обзор типа в типе
GHC 8 расширяет идею полиморфизма видов, объявляя, что типы и виды фактически тождественны. Ничто в GHC не различает типы и виды. Другой способ взглянуть на это — тип Bool и «повышенный вид» Bool фактически идентичны. (Обратите внимание, что термин True и тип 'True по-прежнему различаются, поскольку первый может использоваться в выражениях, а второй — в типах.) Это отсутствие различия между типами и видами является отличительной чертой языков с зависимыми типами. Полностью зависимые типы также устраняют различие между выражениями и типами, но это тема для другого обсуждения в GHC.
Одно упрощение, разрешенное сочетанием типов и видов, заключается в том, что тип Type — просто Type. Хотя аксиома Type :: Type может привести к невыполнению, это не проблема в GHC, так как у нас уже есть другие способы получения бесконечных циклов как в типах, так и в выражениях. Это решение (среди многих других) *действительно* означает, что несмотря на выразительность системы типов GHC, «доказательство», написанное на Haskell, не является неопровержимым математическим доказательством. GHC гарантирует только частичную корректность, что если ваши программы компилируются и выполняются до завершения, их результаты действительно имеют назначенные типы. Он не делает никаких заявлений о программах, которые не завершаются за конечное время.
Чтобы узнать больше об этом решении и разработке GHC «под капотом», ознакомьтесь со статьей, в которой вводится эта система видов в GHC/Haskell.
6.4.13.3. Принципы вывода вида
Как правило, когда PolyKinds включено, GHC пытается вывести наиболее общий вид для объявления. Во многих случаях (например, в объявлении типа данных) определение имеет правую часть, которая информирует вывод вида. Но это не всегда так. Рассмотрим
type family F a
Объявления семейств типов не имеют правой части, но GHC всё равно должен вывести вид для F. Поскольку ограничений нет, он мог бы вывести F :: forall k1 k2. k1 -> k2, но это кажется *слишком* полиморфным. Поэтому GHC по умолчанию устанавливает эти совершенно неограниченные переменные вида в Type и получаем F :: Type -> Type. Вы всё ещё можете объявить F полиморфным по виду, используя виды сигнатур:
type family F1 a -- F1 :: Type -> Type type family F2 (a :: k) -- F2 :: forall k. k -> Type type family F3 a :: k -- F3 :: forall k. Type -> k type family F4 (a :: k1) :: k2 -- F4 :: forall k1 k2. k1 -> k2
Общий принцип таков:
- Если есть правая часть, GHC выводит наиболее полиморфный вид, совместимый с правой частью. Примеры: обычные объявления типов данных и GADTs, объявления классов. В случае объявления класса роль «правой части» играют сигнатуры методов класса.
- Если правой части нет, GHC по умолчанию устанавливает аргумент и результат вида в
Type, если не указано иное с помощью вида сигнатуры. Примеры: объявления данных и открытых семейств типов.
Это правило иногда имеет неожиданные последствия (см. #10132).
class C a where -- Class declarations are generalised
-- so C :: forall k. k -> Constraint
data D1 a -- No right hand side for these two family
type F1 a -- declarations, but the class forces (a :: k)
-- so D1, F1 :: forall k. k -> Type
data D2 a -- No right-hand side so D2 :: Type -> Type
type F2 a -- No right-hand side so F2 :: Type -> Type
Полиморфизм видов из объявления класса делает D1 полиморфным по виду, но не D2; и аналогично F1, F2.
6.4.13.4. Вывод вида в сигнатурах типов
При проверке вида GHC рассматривает только то, что написано в типе, когда определяет, как обобщить вид типа.
Например, рассмотрим эти определения (с ScopedTypeVariables):
data Proxy a -- Proxy :: forall k. k -> Type p :: forall a. Proxy a p = Proxy :: Proxy (a :: Type)
GHC сообщает об ошибке, что вид a должен быть переменной вида k, а не Type. Это потому, что, глядя на сигнатуру типа forall a. Proxy a, GHC предполагает, что вид a должен быть обобщён, а не ограничен Type. Определение функции затем отклоняется за избыточной специфичностью по сравнению со своей сигнатурой типа.
6.4.13.5. Явное квантификация вида
Включенное с помощью PolyKinds, GHC поддерживает явное квантификацию вида, как в этих примерах:
data Proxy :: forall k. k -> Type f :: (forall k (a :: k). Proxy a -> ()) -> Int
Обратите внимание, что во втором примере есть forall, которая связывает как вид k так и переменную типа a вида k. В общем случае нет ограничений на то, насколько глубоко может работать такая зависимость. Однако зависимость должна быть правильно ограничена: forall (a :: k) k. ... — это ошибка.