Вот быстрое и грязное объяснение в простых выражениях, с которым вы, вероятно, уже знакомы.
Ключевое слово forall
действительно используется только одним способом в Haskell. Когда ты видишь это, это всегда означает одно и то же.
Универсальное количественное определение
A универсально выраженный тип является типом формы forall a. f a
. Значение этого типа можно рассматривать как функцию , которая принимает тип a
в качестве аргумента и возвращает значение типа f a
. За исключением того, что в Haskell эти аргументы типа неявно передаются системой типов. Эта «функция» должна давать вам одно и то же значение независимо от того, какой тип она получает, поэтому значение равно polymorphic .
Например, рассмотрим тип forall a. [a]
. Значение этого типа принимает другой тип a
и возвращает список элементов того же типа a
. Конечно, есть только одна возможная реализация. Это должно было бы дать вам пустой список, потому что a
может быть абсолютно любого типа. Пустой список - это единственное значение списка, которое полиморфно в своем типе элемента (так как у него нет элементов).
или тип forall a. a -> a
. Вызывающая сторона такой функции предоставляет тип a
и значение типа a
. Затем реализация должна вернуть значение того же типа a
. Есть только одна возможная реализация снова. Он должен будет вернуть то же значение, которое ему было дано.
Экзистенциальное количественное определение
экзистенциально квантифицированный тип будет иметь форму exists a. f a
, если Haskell поддержит эту запись. Значение этого типа можно представить как пару (или «продукт»), состоящую из типа a
и значения типа f a
.
Например, если у вас есть значение типа exists a. [a]
, у вас есть список элементов некоторого типа. Это может быть любой тип, но даже если вы не знаете, что это, вы многое можете сделать с таким списком. Вы можете изменить его, или вы можете сосчитать количество элементов, или выполнить любую другую операцию со списком, которая не зависит от типа элементов.
ОК, подождите минутку. Почему Haskell использует forall
для обозначения «экзистенциального» типа, подобного следующему?
data ShowBox = forall s. Show s => SB s
Это может сбивать с толку, но в действительности описывает тип конструктора данных SB
:
SB :: forall s. Show s => s -> ShowBox
После создания вы можете думать о значении типа ShowBox
как о двух вещах. Это тип s
вместе со значением типа s
. Другими словами, это значение экзистенциально квантифицированного типа. ShowBox
действительно можно записать как exists s. Show s => s
, если Haskell поддерживает эту запись.
runST
и друзья
Учитывая это, чем они отличаются?
foo :: (forall a. a -> a) -> (Char,Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
Давайте сначала возьмем bar
. Он принимает тип a
и функцию типа a -> a
и создает значение типа (Char, Bool)
. Мы могли бы выбрать Int
в качестве a
и дать ему функцию типа Int -> Int
, например. Но foo
отличается. Требуется, чтобы реализация foo
могла передавать любой тип, который она хочет, в функцию, которую мы ей передаем. Таким образом, единственная функция, которую мы могли бы разумно дать, это id
.
Теперь мы должны уметь понимать значение типа runST
:
runST :: forall a. (forall s. ST s a) -> a
Таким образом, runST
должен иметь возможность создавать значение типа a
, независимо от того, какой тип мы задаем как a
. Для этого ему нужен аргумент типа forall s. ST s a
, который под капотом является просто функцией типа forall s. s -> (a, s)
. Затем эта функция должна иметь возможность выдавать значение типа (a, s)
независимо от того, какой тип реализация runST
решит дать как s
.
ОК, и что?Преимущество состоит в том, что это накладывает ограничение на вызывающего runST
в том, что тип a
вообще не может включать тип s
.Вы не можете передать ему значение типа ST s [s]
, например.На практике это означает, что реализация runST
может выполнять мутацию со значением типа s
.Система типов гарантирует, что эта мутация является локальной для реализации runST
.
. Тип runST
является примером полиморфного типа * 2-го ранга , поскольку тип егоАргумент содержит квантификатор forall
.Тип foo
выше также имеет ранг 2. Обычный полиморфный тип, такой же как у bar
, имеет ранг 1, но становится рангом 2, если требуется, чтобы типы аргументов были полиморфными, со своими собственнымиforall
квантификатор.И если функция принимает аргументы ранга 2, то ее тип - ранг 3 и так далее.Как правило, тип, который принимает полиморфные аргументы ранга n
, имеет ранг n + 1
.