Что делает ключевое слово `forall` в Haskell / GHC?

Я начинаю понимать, как ключевое слово forall используется в так называемых «экзистенциальных типах» следующим образом:

 data ShowBox = forall s. Show s => SB s 

Однако это только подмножество того, как используется forall и я просто не могу обдумать его использование в таких вещах:

 runST :: forall a. (forall s. ST sa) -> a 

Или объяснять, почему это разные:

 foo :: (forall a. a -> a) -> (Char,Bool) bar :: forall a. ((a -> a) -> (Char, Bool)) 

Или весь материал RankNTypes

Я предпочитаю четкий, свободный от жаргона английский, а не те языки, которые нормальны в академической среде. Большинство объяснений, которые я пытаюсь прочитать на этом (те, которые я могу найти через поисковые системы), имеют следующие проблемы:

  1. Они неполные. Они объясняют одну часть использования этого ключевого слова (например, «экзистенциальные типы»), что заставляет меня чувствовать себя счастливым, пока я не прочитаю код, который использует его совершенно по-другому (например, runST , foo и bar выше).
  2. Они плотно заполнены предположениями, что я прочитал последние в любой отрасли дискретной математики, теории категорий или абстрактной алгебры, популярной на этой неделе. (Если я никогда не буду читать слова «проконсультироваться с документом, независимо от деталей реализации», это будет слишком рано).
  3. Они написаны так, что часто превращают даже простые понятия в извращенно извращенную и трещиноватую грамматику и семантику.

Так…

На вопрос. Может ли кто-нибудь полностью объяснить ключевое слово forall на понятном, простом английском (или, если оно существует где-то, указать на такое ясное объяснение, которое я пропустил), который не предполагает, что я математик, погруженный в жаргон?


Отредактировано для добавления:

Ниже были два ответа из более качественных, но, к сожалению, я могу выбрать только один из них. Ответ Нормана был подробным и полезным, объясняя вещи таким образом, чтобы показать некоторые теоретические основы forall и в то же время показать некоторые из практических последствий этого. Ответ yairchu охватывал область, о которой никто не упоминал (переменные типа области), и проиллюстрировал все концепции с кодом и сессией GHCi. Если бы можно было выбрать как лучшее, я бы это сделал. К сожалению, я не могу и, внимательно посмотрев на оба ответа, решил, что яирчу слегка оторвался от Нормана из-за иллюстративного кода и объяснения. Однако это немного несправедливо, потому что на самом деле мне нужны оба ответа, чтобы понять это до такой степени, что forall не оставляет меня со слабым чувством страха, когда я вижу его в сигнатуре типа.

Начнем с примера кода:

 foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b foob postProcess onNothin onJust mval = postProcess val where val :: b val = maybe onNothin onJust mval 

Этот код не компилируется (синтаксическая ошибка) в простой Haskell 98. Для поддержки ключевого слова forall требуется расширение.

В принципе, для ключевого слова forall (или, по крайней мере, так кажется ) есть 3 разных общих применения, и у каждого есть собственное расширение Haskell: ScopedTypeVariables , RankNTypes / Rank2Types , ExistentialQuantification .

Вышеприведенный код не получает синтаксическую ошибку с включенной поддержкой, но только проверки типов с включенными ScopedTypeVariables .

Переменные типа с областью действия:

Скопированные переменные типа помогают указывать типы для кода внутри where клаузулы. Это делает b в val :: b тем же самым, что и b в foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b .

Смущающая точка : вы можете услышать, что, когда вы опускаете forall из типа, это на самом деле все еще неявно. ( из ответа Нормана: «Обычно эти языки опускают forall из полиморфных типов» ). Это утверждение верно, но оно относится к другим применениям forall , а не к использованию ScopedTypeVariables .

Ранг-N-типов:

Начнем с того, что mayb :: b -> (a -> b) -> Maybe a -> b эквивалентен mayb :: forall a b. b -> (a -> b) -> Maybe a -> b mayb :: forall a b. b -> (a -> b) -> Maybe a -> b , за исключением случаев, когда включена ScopedTypeVariables .

Это означает, что он работает для всех a и b .

Предположим, вы хотите сделать что-то подобное.

 ghci> let putInList x = [x] ghci> liftTup putInList (5, "Blah") ([5], ["Blah"]) 

Какой должен быть тип этого liftTup ? Это liftTup :: (forall x. x -> fx) -> (a, b) -> (fa, fb) . Чтобы понять, почему, попробуем его закодировать:

 ghci> let liftTup liftFunc (a, b) = (liftFunc a, liftFunc b) ghci> liftTup (\x -> [x]) (5, "Hello") No instance for (Num [Char]) ... ghci> -- huh? ghci> :t liftTup liftTup :: (t -> t1) -> (t, t) -> (t1, t1) 

«Хм … почему GHC заключает, что кортеж должен содержать два одинаковых типа? Скажем, им не обязательно быть»

 -- test.hs liftTup :: (x -> fx) -> (a, b) -> (fa, fb) liftTup liftFunc (t, v) = (liftFunc t, liftFunc v) ghci> :l test.hs Couldnt match expected type 'x' against inferred type 'b' ... 

Хм. поэтому здесь ghc не позволяет применять liftFunc на v потому что v :: b и liftFunc хочет x . Мы действительно хотим, чтобы наша функция получала функцию, которая принимает любые возможные x !

 {-# LANGUAGE RankNTypes #-} liftTup :: (forall x. x -> fx) -> (a, b) -> (fa, fb) liftTup liftFunc (t, v) = (liftFunc t, liftFunc v) 

Так что это не liftTup который работает для всех x , это функция, которую он получает, что делает.

Экзистенциальная количественная оценка:

Давайте воспользуемся примером:

 -- test.hs {-# LANGUAGE ExistentialQuantification #-} data EQList = forall a. EQList [a] eqListLen :: EQList -> Int eqListLen (EQList x) = length x ghci> :l test.hs ghci> eqListLen $ EQList ["Hello", "World"] 2 

Как это отличается от Rank-N-Types?

 ghci> :set -XRankNTypes ghci> length (["Hello", "World"] :: forall a. [a]) Couldnt match expected type 'a' against inferred type '[Char]' ... 

С Rank-N-Type, forall a означает, что ваше выражение должно соответствовать всем возможным. Например:

 ghci> length ([] :: forall a. [a]) 0 

Пустой список работает как список любого типа.

Таким образом, с помощью экзистенциальной квантификации, для forall s в определениях data означает, что содержащееся значение может быть любого подходящего типа, а не того, что он должен быть всех подходящих типов.

Может ли кто-нибудь полностью объяснить ключевое слово forall на понятном, простом английском языке?

Нет. (Может, Дон Стюарт может.)

Вот преgradleы для простого, ясного объяснения или forall :

  • Это квантификатор. У вас есть хотя бы небольшая логика (исчисление предикатов), чтобы увидеть универсальный или экзистенциальный квантификатор. Если вы никогда не видели исчисления предикатов или не устраиваете квантификаторы (и я видел учеников во время квалификационных экзаменов PhD, которые неудобны), то для вас нет простого объяснения forall .

  • Это квантор типа . Если вы не видели System F и получили некоторую практику написания полиморфных типов, вы обнаружите, что все это запутывает. Опыт с Haskell или ML недостаточен, потому что обычно эти языки опускают forall из полиморфных типов. (На мой взгляд, это ошибка дизайна языка.)

  • В Haskell, в частности, forall используется способами, которые я нахожу смутными. (Я не теоретик типа, но моя работа приводит меня к контакту с теорией типов, и мне это очень удобно.) Для меня основным источником путаницы является то, что forall используется для кодирования типа что я сам предпочел бы писать с exists . Это оправдано сложным битом изоморфизма типа с кванторами и стрелками, и каждый раз, когда я хочу это понять, мне приходится искать вещи и сам изоморфизм.

    Если вам не нравится идея изоморфизма типа, или если у вас нет практики думать о изоморфизмах типов, это использование forall будет замалчивать вас.

  • Хотя общая концепция forall всегда одна и та же (привязка к вводу переменной типа), детали разных применений могут значительно различаться. Неформальный английский – не очень хороший инструмент для объяснения вариантов. Чтобы действительно понять, что происходит, вам нужна математика. В этом случае соответствующую математику можно найти в вводных текстах и языках программирования Бенджамина Пирса, что является очень хорошей книгой.

Что касается ваших конкретных примеров,

  • runST должен сделать вашу голову runST . Типы более высокого ранга (forall слева от стрелки) редко встречаются в дикой природе. Я рекомендую вам прочитать статью, в которой представлен runST : «Lazy Functional State Threads» . Это действительно хорошая статья, и она даст вам гораздо лучшую интуицию для типа runST в частности и для типов более высокого ранга в целом. Объяснение занимает несколько страниц, это очень хорошо сделано, и я не собираюсь его конденсировать.

  • Рассматривать

     foo :: (forall a. a -> a) -> (Char,Bool) bar :: forall a. ((a -> a) -> (Char, Bool)) 

    Если я вызываю bar , я могу просто выбрать любой тип a который мне нравится, и я могу передать ему функцию от типа a до типа a . Например, я могу передать функцию (+1) или функцию reverse . Вы можете думать о forall как о том, что «я сейчас выбираю тип». (Техническое слово для выбора типа создается .)

    Ограничения на вызов foo гораздо более строгие: аргумент foo должен быть полиморфной функцией. С этим типом единственные функции, которые я могу передать foo – это id или функция, которая всегда расходится или ошибки, например undefined . Причина в том, что с foo , forall находится слева от стрелки, так как вызывающий foo я не могу выбрать, что a такое – скорее это реализация foo которая выбирает то, что есть. Поскольку forall находится слева от стрелки, а не выше стрелки, как в bar , создание объекта происходит в теле функции, а не на сайте вызова.

Резюме. Полное объяснение ключевого слова forall требует математики и может быть понято только тем, кто изучал математику. Даже частичные объяснения трудно понять без математики. Но, может быть, мои частичные, не-математические объяснения помогают немного. Пойдите, прочитайте Launchbury и Пейтон Джонс на runST !


Приложение: Жаргон «выше», «ниже», «слева». Они не имеют ничего общего с текстовыми способами, которые пишут типы, и все, что связано с абстрактно-синтаксическими деревьями. В абстрактном синтаксисе forall принимает имя переменной типа, а затем полный тип «ниже» forall. Стрелка принимает два типа (аргумент и тип результата) и формирует новый тип (тип функции). Тип аргумента – «слева от» стрелки; это левый ребенок стрелки в дереве абстрактно-синтаксиса.

Примеры:

  • В forall a . [a] -> [a] forall a . [a] -> [a] , forall находится выше стрелки; что находится слева от стрелки [a] .

  • В

     forall nfex . (forall ex . nex -> f -> Fact xf) -> Block nex -> f -> Fact xf 

    тип в скобках будет называться «forall слева от стрелки». (Я использую такие типы в оптимизаторе, над которым я работаю.)

Мой оригинальный ответ:

Может ли кто-нибудь полностью объяснить ключевое слово forall на понятном, простом английском языке?

Как указывает Норман, очень сложно дать ясное, простое английское объяснение технического термина из теории типов. Мы все пытаемся.

Существует только одна вещь, чтобы помнить о «forall»: она связывает типы с некоторой областью . Как только вы это понимаете, все довольно просто. Это эквивалент «лямбды» (или формы «let») на уровне типа. Норман Рамси использует понятие «левый» / «выше», чтобы передать эту же концепцию сферы в своем превосходном ответе .

Большинство применений «forall» очень просты, и вы можете найти их в Руководстве пользователя GHC, S7.8 , в частности, отличном S7.8.5 на вложенных формах «forall».

В Haskell мы обычно оставляем связующее для типов, когда тип универсально quanitified, например:

 length :: forall a. [a] -> Int 

эквивалентно:

 length :: [a] -> Int 

Вот и все.

Поскольку теперь вы можете привязать переменные типа к некоторой области видимости, вы можете иметь области, отличные от верхнего уровня (« универсально квантифицированные »), как и ваш первый пример, где переменная типа видна только в структуре данных. Это позволяет скрывать типы (« экзистенциальные типы »). Или мы можем иметь произвольное вложение привязок («ранг N типов»).

Чтобы глубоко понять системы типов, вам нужно будет изучить некоторые жаргоны. Такова природа компьютерной науки. Однако простое использование, как и выше, должно быть понято интуитивно, по аналогии с «let» на уровне значений. Отличное введение – Launchbury и Peyton Jones .

Они плотно заполнены предположениями, что я прочитал последние в любой отрасли дискретной математики, теории категорий или абстрактной алгебры, популярной на этой неделе. (Если я никогда не буду читать слова «проконсультироваться с документом, независимо от деталей реализации», это будет слишком рано).

Er, а как насчет простой логики первого порядка? forall довольно четко относится к универсальной количественной оценке , и в этом контексте термин экзистенциальный имеет больше смысла, хотя было бы менее неудобно, если бы exists ключевое слово exist. Независимо от того, действительно ли квантификация является универсальной или экзистенциальной, зависит от размещения квантора относительно того, где переменные используются на какой стороне стрелки функции, и все это немного запутывает.

Таким образом, если это не помогает, или если вы просто не любите символическую логику, из более функциональной перспективы программирования вы можете думать о переменных типа как о простом (неявном) типе параметров функции. Функции, принимающие параметры типа в этом смысле, традиционно записываются с использованием капитала lambda по любой причине, которую я напишу здесь как /\ .

Итак, рассмотрим функцию id :

 id :: forall a. a -> a id x = x 

Мы можем переписать его как lambdas, перемещая «параметр типа» из сигнатуры типа и добавляя annotations типа строки:

 id = /\a -> (\x -> x) :: a -> a 

Вот то же самое для const :

 const = /\ab -> (\xy -> x) :: a -> b -> a 

Таким образом, ваша функция bar может быть примерно такой:

 bar = /\a -> (\f -> ('t', True)) :: (a -> a) -> (Char, Bool) 

Обратите внимание, что тип функции, заданной для bar в качестве аргумента, зависит от параметра типа bar . Подумайте, есть ли у вас что-то вроде этого:

 bar2 = /\a -> (\f -> (f 't', True)) :: (a -> a) -> (Char, Bool) 

Здесь bar2 применяет функцию к чему-то типа Char , поэтому предоставление bar2 любому типу параметра, отличному от Char , приведет к ошибке типа.

С другой стороны, вот что может выглядеть foo :

 foo = (\f -> (f Char 't', f Bool True)) 

В отличие от bar , foo фактически не принимает никаких параметров типа вообще! Он принимает функцию, которая сама принимает параметр типа, затем применяет эту функцию к двум различным типам.

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


Post scriptum : Возможно, вы можете удивиться – теперь, когда мы думаем о функциях, принимающих параметры типа, почему мы не можем сделать что-то более интересное с этими параметрами, чем помещать их в подпись типа? Ответ в том, что мы можем!

Функция, которая ставит переменные типа вместе с меткой и возвращает новый тип, является конструктором типа , который вы могли бы написать примерно так:

 Either = /\ab -> ... 

Но нам понадобится совершенно новая нотация, потому что способ записи такого типа, как и Either ab , уже наводит на мысль о «применении функции Either к этим параметрам».

С другой стороны, функция типа «шаблон соответствует» по своим параметрам типа, возвращающая разные значения для разных типов, является методом classа типа . Небольшое расширение для моего /\ синтаксиса выше предлагает что-то вроде этого:

 fmap = /\ fab -> case f of Maybe -> (\gx -> case x of Just y -> Just bgy Nothing -> Nothing b) :: (a -> b) -> Maybe a -> Maybe b [] -> (\gx -> case x of (y:ys) -> gy : fmap [] abg ys [] -> [] b) :: (a -> b) -> [a] -> [b] 

Лично я считаю, что я предпочитаю синтаксис Haskell …

Функция, которая «соответствует шаблону» своим параметрам типа и возвращает произвольный существующий тип, является семейством типов или функциональной зависимостью – в первом случае она даже уже очень похожа на определение функции.

Вот краткое и грязное объяснение в простых выражениях, с которыми вы, вероятно, уже знакомы.

Ключевое слово forall действительно используется только одним способом в Haskell. Это всегда означает то же самое, когда вы это видите.

Универсальная количественная оценка

Универсально квантифицированный тип является типом формы forall a. fa forall a. fa . Значение этого типа можно рассматривать как функцию, которая принимает тип a как свой аргумент и возвращает значение типа fa . За исключением того, что в Haskell эти аргументы типа передаются неявно системой типов. Эта «функция» должна давать вам одно и то же значение независимо от того, какой тип он получает, поэтому значение является полиморфным .

Например, рассмотрим тип forall a. [a] forall a. [a] . Значение этого типа принимает другой тип a и возвращает вам список элементов того же типа a . Конечно, есть только одна возможная реализация. Это должно было бы дать вам пустой список, потому что a может быть абсолютно любым типом. Пустой список – это единственное значение списка, которое является полиморфным по типу элемента (поскольку оно не имеет элементов).

Или тип forall a. a -> a forall a. a -> a . Вызывающий из такой функции предоставляет тип a и значение типа a . Затем реализация должна вернуть значение того же типа a . Повторяется только одна возможная реализация. Он должен был бы вернуть то же значение, что и он.

Экзистенциальная количественная оценка

Экзистенциально квантованный тип имел бы форму exists a. fa exists a. fa , если Haskell поддерживает эту нотацию. Значение такого типа можно рассматривать как пару (или «продукт»), состоящую из типа a и значения типа fa .

Например, если у вас есть значение типа exists a. [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 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 :

 runST :: forall a. (forall s. ST sa) -> a 

Таким образом, runST должен иметь возможность создавать значение типа a , независимо от того, какой тип мы даем как a . Для этого ему нужен аргумент типа forall s. ST sa forall s. ST sa который под капотом – это просто функция типа forall s. s -> (a, s) 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 .

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

Вероятно, лучше всего просто прочитать и понять эти две вещи отдельно, вместо того, чтобы пытаться объяснить, почему forall является подходящим битом синтаксиса одновременно.

Может ли кто-нибудь полностью объяснить ключевое слово forall на понятном, простом английском (или, если оно существует где-то, указать на такое ясное объяснение, которое я пропустил), который не предполагает, что я математик, погруженный в жаргон?

Я попытаюсь объяснить просто смысл и, возможно, приложение forall в контексте Haskell и его систем типов.

Но прежде чем вы поймете, что я хотел бы направить вас к очень доступному и приятному разговору Рунара Бьярнасона под названием « Ограничения освобождения, ограничения свободы ». Беседа полна примеров из реальных случаев использования в мире, а также примеров в Scala для поддержки этого утверждения, хотя он не упоминает forall . Я попытаюсь объяснить дальнейшую перспективу ниже.

  CONSTRAINTS LIBERATE, LIBERTIES CONSTRAIN 

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

Теперь очень распространенным примером, показывающим выразительность системы типа Haskell, является подпись этого типа:

foo :: a -> a

Говорят, что, учитывая эту сигнатуру типа, существует только одна функция, которая может удовлетворять этому типу, и это функция identity или то, что является более популярным id .

На начальных этапах обучения Haskell я всегда задавался вопросом о следующих функциях:

 foo 5 = 6 foo True = False 

они оба удовлетворяют вышеуказанной сигнатуре типа, то почему люди Haskell утверждают, что это только один id который удовлетворяет сигнатуре типа?

Это связано с тем, что в сигнатуре типа имеется скрытое forall скрытие. Фактический тип:

 id :: forall a. a -> a 

Итак, давайте вернемся к утверждению: ограничения освобождают, свободы ограничивают

Переведя это в систему типов, это утверждение будет выглядеть следующим образом:

Ограничение уровня типа становится свободой на уровне термина

а также

Свобода на уровне типа становится ограничением на уровне термина


Попробуем доказать первое утверждение:

Ограничение уровня типа.

Таким образом, ограничение на подпись типа

 foo :: (Num a) => a -> a 

становится свободой на уровне термина, дает нам свободу или гибкость, чтобы написать все эти

 foo 5 = 6 foo 4 = 2 foo 7 = 9 ... 

То же самое можно наблюдать путем ограничения a с любым другим типом и т. Д.

Итак, теперь эта подпись типа: foo :: (Num a) => a -> a означает:

 ∃a , st a -> a, ∀a ∈ Num 

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

Следовательно, мы можем видеть добавление ограничения (что a должно принадлежать множеству чисел), освобождает уровень термина, чтобы иметь несколько возможных реализаций.


Теперь перейдем ко второму утверждению и к тому, которое фактически содержит объяснение forall :

Свобода на уровне типа становится ограничением на уровне термина

Теперь давайте освободим функцию на уровне типа:

 foo :: forall a. a -> a 

Теперь это означает:

 ∀a , a -> a 

это означает, что реализация такой подписи должна быть такой, чтобы она a -> a при любых обстоятельствах.

Итак, теперь это начинает сдерживать нас на уровне термина. Мы больше не можем писать

 foo 5 = 7 

потому что эта реализация не будет удовлетворять, если мы поместим Bool . a может быть Char или [Char] или пользовательский тип данных. При любых обстоятельствах он должен возвращать нечто похожее. Эта свобода на уровне типа – это то, что известно как Универсальная количественная оценка, и единственная функция, которая может удовлетворить это

 foo a = a 

который широко известен как функция identity


Следовательно, forallliberty на уровне типа, фактическая цель которого заключается в том, чтобы constrain уровень термина конкретной реализацией.

Как экзистенциально экзистенциально?

При экзистенциальной квантификации, в forall в определениях data означает, что содержащееся значение может быть любого подходящего типа, а не того, что он должен быть всех подходящих типов. – ответ Ячиру

Объяснение того, почему forall в определениях data изоморфно (exists a. a) (Псевдо-Haskell), можно найти в «Haskell / Existently quantified types» от wikibooks .

Ниже приводится краткое стенографическое резюме:

 data T = forall a. MkT a -- an existential datatype MkT :: forall a. a -> T -- the type of the existential constructor 

Когда сопоставление / деконструирование MkT x , каков тип x ?

 foo (MkT x) = ... -- -- what is the type of x? 

x может быть любым типом (как указано в forall ), и поэтому это тип:

 x :: exists a. a -- (pseudo-Haskell) 

Следовательно, изоморфны:

 data T = forall a. MkT a -- an existential datatype data T = MkT (exists a. a) -- (pseudo-Haskell) 

forall означает forall

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

Значение forall означает, что определение значения или функции должно быть полиморфным.

Если определяемая вещь является полиморфным значением , то это означает, что значение должно быть действительным для всех подходящих a , что является довольно ограничительным.

Если определяемая вещь является полиморфной функцией , то это означает, что функция должна быть действительной для всех подходящих a , что не является ограничительным, потому что только потому, что функция является полиморфной, не означает, что применяемый параметр должен быть полиморфным. То есть, если функция действительна для всех a , то, наоборот, любое подходящее a может быть применено к функции. Однако тип параметра можно выбрать только один раз в определении функции.

Если forall находится внутри типа параметра функции (т. Rank2Type ), то это означает, что прикладной параметр должен быть действительно полиморфным, чтобы быть совместимым с идеей forall означает, что определение является полиморфным. В этом случае тип параметра можно выбрать более одного раза в определении функции ( «и выбирается реализацией функции», как указывает Норман )

Поэтому причина, по которой определения экзистенциальных data позволяет любому a , потому что конструктор данных является полиморфной функцией :

 MkT :: forall a. a -> T 

вид MkT :: a -> *

Это означает, что любой a может быть применен к функции. В противоположность, скажем, полиморфному значению :

 valueT :: forall a. [a] 

вид valueT :: a

Это означает, что определение valueT должно быть полиморфным. В этом случае значение valueT может быть определено как пустой список [] всех типов.

 [] :: [t] 

Различия

Несмотря на то, что значение forall является последовательным в ExistentialQuantification и RankNType , экзистенциальность имеет разницу, поскольку конструктор data может использоваться при сопоставлении с образцом. Как описано в руководстве пользователя ghc :

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

  • Строка не была признана действительным DateTime "format dd / MM / yyyy"
  • Сколько стоит слишком много с ключевым словом C ++ 11 auto?
  • Возвращать определенный тип внутри Haskell
  • Поиск типа объекта в C ++
  • Использовать разницу типов
  • make arrayList.toArray () возвращает более конкретные типы
  • Что такое типы POD в C ++?
  • .NET. Определите тип этого «classа» в статическом методе
  • Каков тип строкового литерала в C ++?
  • Типы данных PostgreSQL и C #
  • Буферный тип данных C99?
  • Давайте будем гением компьютера.