-
TypeApplications -
Разрешить использование синтаксиса применения типов.
Расширение TypeApplications позволяет использовать видимое применение типов в выражениях. Вот пример: show (read @Int "5"). @Int — это видимое применение типа; оно задаёт значение переменной типа в типе read.
Видимое применение типа предваряется знаком @. (Для разбора синтаксиса знак @ должен предваряться неидентификатором, обычно пробелом. Например, read@Int 5 не будет распознан.) Оно может использоваться, когда известен полный полиморфный тип функции. Если функция является идентификатором (обычный случай), её тип считается известным только тогда, когда идентификатору задана сигнатура типа. Если у идентификатора нет сигнатуры типа, видимое применение типа использовать нельзя.
GHC также допускает видимое применение рода, где пользователи могут объявлять аргументы рода, которые должны быть подставлены в полиморфные по роду случаи. Его использование аналогично видимому применению типа на уровне терминов, как указано выше.
Помимо применения видимого типа в терминах и типах, синтаксис применения типа может использоваться в шаблонах сопоставления с конструктором данных, чтобы привязать переменные типа к типу этого конструктора.
6.4.16.1. Выводные и заданные переменные типа
GHC отслеживает различие между переменными типа, которые мы называем выведенными и заданными. Только заданные переменные типа доступны для подстановки при видимом применении типов. Пример хорошо это иллюстрирует:
f :: (Eq b, Eq a) => a -> b -> Bool f x y = (x == x) && (y == y) g x y = (x == x) && (y == y)
Функции f и g имеют одинаковое тело, но только f имеет сигнатуру типа. Когда GHC вычисляет, как обработать видимое применение типа, он должен знать, какую переменную подставить. Поэтому он должен иметь возможность задать порядок переменных типа в типе функции.
Если пользователь предоставил сигнатуру типа, как в f, то это легко: мы просто берём порядок из сигнатуры типа, слева направо и используем первое вхождение переменной для выбора её позиции в порядке. Таким образом, переменные в f будут b, затем a.
В отличие от этого, для g нет надёжного способа сделать это; мы не будем знать, Eq a или Eq b будут указаны первыми в ограничении в типе g. Для того, чтобы видимое применение типов было надёжным между выпусками GHC, мы запрещаем его использование с g.
Мы говорим, что переменные типа в f являются заданными, а те в g — выведенными. Общее правило таково: если пользователь написал переменную типа в исходной программе, она является заданной; если нет, она является выведенной.
Это правило применяется и к объявлениям типов данных. Например, если у нас есть data Proxy a = Proxy (и PolyKinds включено), то a будет присвоено родовое значение k, где k — новая переменная рода. Поскольку k не был написан пользователем, он будет недоступен для применения типа в типе конструктора Proxy; доступны только a.
Выводные переменные печатаются в фигурных скобках. Таким образом, тип конструктора данных Proxy из предыдущего примера — forall {k} (a :: k). Proxy a. Мы можем наблюдать это поведение в сеансе GHCi:
> :set -XTypeApplications -fprint-explicit-foralls
> let myLength1 :: Foldable f => f a -> Int; myLength1 = length
> :type myLength1
myLength1 :: forall (f :: * -> *) a. Foldable f => f a -> Int
> let myLength2 = length
> :type myLength2
myLength2 :: forall {t :: * -> *} {a}. Foldable t => t a -> Int
> :type myLength2 @[]
<interactive>:1:1: error:
• Cannot apply expression of type ‘t0 a0 -> Int’
to a visible type argument ‘[]’
• In the expression: myLength2 @[]
Обратите внимание, что поскольку myLength1 был определён с явной сигнатурой типа, :type сообщает, что все его переменные типа доступны для применения типа. С другой стороны, myLength2 не имела сигнатуры типа. В результате все его переменные типа окружены фигурными скобками, и попытка использовать видимое применение типа с myLength2 терпит неудачу.
6.4.16.2. Порядок заданных переменных
В простом случае предыдущего раздела мы можем сказать, что заданные переменные появляются в порядке слева направо. Однако не все случаи так просты. Вот правила в более сложных случаях:
- Если тип идентификатора имеет
forall, то порядок переменных типа, как написано вforall, сохраняется. -
Если какие-либо из переменных зависят от других переменных (то есть, если некоторые из переменных являются родовыми переменными), переменные переупорядочиваются так, что родовые переменные предшествуют переменным типа, сохраняя порядок слева направо насколько это возможно. То есть GHC выполняет устойчивую топологическую сортировку переменных. Пример:
h :: Proxy (a :: (j, k)) -> Proxy (b :: Proxy a) -> () -- as if h :: forall j k a b. ...
В этом примере
aзависит отjиk, аbзависит отa. Несмотря на то, чтоaлексически предшествуетjиk,jиkквантифицируются первыми, потому чтоaзависит отjиk. Кроме того, обратите внимание, чтоjиkне переупорядочиваются друг относительно друга, даже если это не нарушит зависимостей.Здесь «устойчивая топологическая сортировка» означает, что мы выполняем этот алгоритм (который мы называем ScopedSort):
- Обрабатываем входной список переменных типа слева направо с курсором.
- Если переменная
vв курсоре зависит от какой-либо предыдущей переменнойw, переместитьvсразу перед самой левой такойw.
- Аргументы типа методов класса включают переменные типа класса, за которыми следуют любые переменные, в которых метод является полиморфным. Таким образом,
class Monad m where return :: a -> m aозначает, что аргументы типаreturn—m, a. - С помощью расширения
RankNTypes(Лексически-определённые переменные типа) можно объявлять аргументы типа не в начале типа. Например, мы можем иметьpair :: forall a. a -> forall b. b -> (a, b)и затем сказатьpair @Bool True @Char, что будет иметь типChar -> (Bool, Char). - Частичные сигнатуры типов (Частичные сигнатуры типов) хорошо работают с видимым применением типов. Если вы хотите указать только второй аргумент типа для
wurble, тогда вы можете сказатьwurble @_ @Int. Первый аргумент является подстановочным, как и в частичной сигнатуре типа. Однако, если используется видимое применение типа/видимое применение рода, не нужно указыватьPartialTypeSignatures, и ваш код не сгенерирует предупреждение, сообщающее об опущенном типе.
Раздел этой справки по полиморфизму рода описывает, как упорядочиваются переменные в объявлениях типов и классов (Вывод порядка переменных в объявлении типа/класса).