Spec-Zone.ru › Haskell 9

6.11.1. Явная универсальная квантификация (forall)

ExplicitForAll
Подразумевается расширениями:

ScopedTypeVariables, LiberalTypeSynonyms, RankNTypes, ExistentialQuantification

Начиная с версии:

6.12.1

Статус:

Включено в GHC2024, GHC2021

Разрешает использовать ключевое слово forall в местах, где универсальная квантификация подразумевается.

Сигнатуры типов Haskell квантифицируются неявно. При использовании языкового расширения ExplicitForAll ключевое слово forall позволяет точно указать, что это означает. Например:

g :: b -> b

означает следующее:

g :: forall b. (b -> b)

Эти два варианта обрабатываются одинаково, за исключением того, что второй может вводить переменные типов в область видимости (см. Переменные типов с лексической областью видимости).

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

Примечания:

  • Явный forall можно использовать не только в сигнатурах типов, но и в объявлении экземпляра:

    instance forall a. Eq a => Eq [a] where ...
    

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

  • Если включён флаг -Wunused-foralls, будет выдано предупреждение, если в явном операторе forall указана неиспользуемая в остальном переменная типа. Например:

    g :: forall a b. (b -> b)
    

    вызовет предупреждение о неиспользуемой переменной типа a.

6.11.1.1. Правило «forall или ничего»

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

f  :: forall a b. a -> b -> b         -- OK, `a` and `b` are explicitly bound
g  :: forall a. a -> forall b. b -> b -- OK, `a` and `b` are explicitly bound
h  :: forall a. a -> b -> b           -- Rejected, `b` is not in scope

Сигнатуры типов для f, g и h начинаются с внешнего невидимого forall, поэтому каждая переменная типа в этих сигнатурах должна быть явно связана с помощью forall. И f, и g соответствуют правилу «forall или ничего», поскольку в них явно квантифицируются a и b. С другой стороны, в h b явно не квантифицируется, поэтому GHC отклонит эту сигнатуру типа из-за неправильной области видимости.

В местах, где применяется правило «forall или ничего», если у типа нет внешнего невидимого forall, все переменные типов, не связанные явно с помощью forall, квантифицируются неявно. Например:

i :: a -> b -> b             -- `a` and `b` are implicitly quantified
j :: a -> forall b. b -> b   -- `a` is implicitly quantified
k :: (forall a. a -> b -> b) -- `b` is implicitly quantified
type L :: forall a -> b -> b -- `b` is implicitly quantified

GHC примет сигнатуры типов i, j и k, а также сигнатуру вида для L. Обратите внимание:

  • Сигнатура j принимается, несмотря на сочетание неявной и явной квантификации. Пока forall не является внешним, его можно использовать среди неявно связанных переменных типов.
  • Сигнатура k принимается, поскольку внешние скобки означают, что forall не является внешним forall. Правило «forall или ничего» — один из немногих случаев в GHC, когда наличие или отсутствие скобок может иметь семантическое значение!
  • Сигнатура L начинается с внешнего forall, но это видимый forall, а не невидимый forall, поэтому правило «forall или ничего» не применяется.

Правило «forall или ничего» применяется в следующих местах:

  • Объявления сигнатур типов функций, значений и методов классов
  • Аннотации типов выражений
  • Объявления экземпляров
  • Сигнатуры методов по умолчанию
  • Сигнатуры типов в прагме SPECIALIZE или прагме SPECIALIZE instance
  • Самостоятельные сигнатуры видов и полиморфная рекурсия
  • Сигнатуры типов конструкторов обобщённых алгебраических типов данных (GADT)
  • Сигнатуры типов для синонимов шаблонов
  • Объявления экземпляров данных, объявления экземпляров типов, закрытые семейства типов и ассоциированные экземпляры
  • Правила переписывания, в которых переменные типов квантифицируются явно

Примечания:

  • Сигнатуры типов шаблонов — примечательный пример места, где типы не подчиняются правилу «forall или ничего». Например, GHC примет следующий код:

    f (g :: forall a. a -> b) x = g x :: b
    

    Кроме того, правила переписывания не подчиняются правилу «forall или ничего», если их переменные типов не квантифицируются явно:

    {-# RULES "f" forall (g :: forall a. a -> b) x. f g x = g x :: b #-}
    
  • Конструкторы GADT особенно требовательны к своим forall. Помимо соблюдения правила «forall или ничего», конструкторы GADT также запрещают вложенные forall. Например, GHC отклонит следующий GADT:

    data T where
      MkT :: (forall a. a -> b -> T)
    

    Из-за отсутствия внешнего forall в типе MkT переменная b квантифицировалась бы неявно. По сути, это было бы равносильно записи MkT :: forall b. (forall a. a -> b -> T), содержащей вложенные forall. См. Формальный синтаксис GADT.

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/9.14.1/docs/users_guide/exts/explicit_forall.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API