Я начинаю понимать, как forall
ключевое слово используется в так называемых «экзистенциальных типах», например:
data ShowBox = forall s. Show s => SB s
Однако это только часть того, как forall
это используется, и я просто не могу сосредоточиться на его использовании в таких вещах:
runST :: forall a. (forall s. ST s a) -> a
Или объясняя, почему они разные:
foo :: (forall a. a -> a) -> (Char, Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
Или весь RankNTypes
материал ...
Я предпочитаю ясный, свободный от жаргонного языка английский язык, а не тот язык, который является нормальным в академической среде. Большинство объяснений, которые я пытаюсь прочитать по этому поводу (те, которые я могу найти в поисковых системах), имеют следующие проблемы:
- Они неполные. Они объясняют одну часть использования этого ключевого слова (например , «экзистенциальные типы») , которая заставляет меня чувствовать себя счастливым , пока я не прочитал код , который использует его в совершенно по- другому (как
runST
,foo
иbar
выше). - Они переполнены предположениями, которые я читал последними в любой области дискретной математики, теории категорий или абстрактной алгебры, популярной на этой неделе. (Если бы я никогда не читал слова «советоваться бумагу все , что подробности реализации» снова, это будет слишком скоро.)
- Они написаны способами, которые часто превращают даже простые понятия в извилистую и искаженную грамматику и семантику.
Так...
На актуальный вопрос. Кто-нибудь может полностью объяснить forall
ключевое слово на ясном, простом английском языке (или, если оно где-то существует, указать на такое ясное объяснение, которое я пропустил), которое не предполагает, что я математик, погруженный в жаргон?
Отредактировано, чтобы добавить:
Ниже были два выдающихся ответа из более качественных, но, к сожалению, я могу выбрать только один как лучший. Ответ Нормана был подробным и полезным, объясняя вещи таким образом, чтобы показать некоторые теоретические основы forall
и в то же время показать мне некоторые его практические последствия. ответ Яирчуохватил область, которую еще никто не упомянул (переменные типа scoped) и проиллюстрировал все концепции с помощью кода и сеанса GHCi. Было бы возможно выбрать оба, как лучше, я бы. К сожалению, я не могу, и, внимательно изучив оба ответа, я решил, что Яирчу слегка обходит Нормана из-за иллюстративного кода и прилагаемого объяснения. Однако это немного несправедливо, потому что на самом деле мне нужны были оба ответа, чтобы понять это до такой степени, что я forall
не испытываю слабого чувства страха, когда вижу его в сигнатуре типа.
Ответы:
Давайте начнем с примера кода:
Этот код не компилируется (синтаксическая ошибка) в простом Haskell 98. Для поддержки
forall
ключевого слова требуется расширение .В основном, есть 3 различные общие используют для
forall
ключевого слова (или , по крайней мере , так кажется ), и каждый из них имеет свое собственное расширение Haskell:ScopedTypeVariables
,RankNTypes
/Rank2Types
,ExistentialQuantification
.Приведенный выше код не получает синтаксическую ошибку с любым из этих включенных, но только проверки типов с
ScopedTypeVariables
включенным.Переменные типа Scoped:
Переменные типа Scoped помогают определить типы для кода внутри
where
предложений. Это делаетb
вval :: b
том же, что иb
вfoob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b
.Заблуждение : вы можете услышать, что когда вы опускаете
forall
тип, он все еще неявно присутствует. ( из ответа Нормана: «как правило, эти языки не включают в себя полиморфные типы» ). Это утверждение верно, но оно относится к другому использованиюforall
, а не кScopedTypeVariables
использованию.Ранг-N-Type:
Давайте начнем с того, что
mayb :: b -> (a -> b) -> Maybe a -> b
эквивалентноmayb :: forall a b. b -> (a -> b) -> Maybe a -> b
, за исключением, когдаScopedTypeVariables
включен.Это означает, что это работает для всех
a
иb
.Допустим, вы хотите сделать что-то подобное.
Какой должен быть тип этого
liftTup
? ЭтоliftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
. Чтобы понять почему, давайте попробуем закодировать это:«Хм… почему GHC делает вывод, что кортеж должен содержать два одинаковых типа? Давайте скажем, что они не должны быть»
Хм. поэтому здесь GHC не применим
liftFunc
наv
потомуv :: b
иliftFunc
хочетx
. Мы действительно хотим, чтобы наша функция получала функцию, которая принимает все возможныеx
!Так что
liftTup
это работает не для всехx
, а для функции, которую он получает.Экзистенциальная количественная оценка:
Давайте использовать пример:
Чем это отличается от Rank-N-Types?
С Rank-N-Types
forall a
означало, что ваше выражение должно соответствовать всем возможнымa
s. Например:Пустой список работает как список любого типа.
Таким образом, с помощью Existential-Quantification,
forall
s вdata
определениях означает, что содержащееся значение может иметь любой подходящий тип, а не то, что оно должно быть всех подходящих типов.источник
ScopedTypeVariables
кажется, хуже, чем есть. Если вы напишите типb -> (a -> b) -> Maybe a -> b
с этим расширением, он все равно будет в точности эквивалентенforall a b. b -> (a -> b) -> Maybe a -> b
. Тем не менее, если вы хотите сослаться на то же самоеb
(и не неявно количественно) , то вам нужно записать в явном виде количественных версии. В противном случаеSTV
было бы чрезвычайно навязчивое расширение.ScopedTypeVariables
, и я не думаю, что это плохо. imho, это очень полезный инструмент для процесса программирования, особенно для начинающих пользователей Haskell, и я благодарен, что он существует.Нет. (Может, Дон Стюарт сможет.)
Вот барьеры для простого, ясного объяснения или
forall
:Это квантификатор. У вас должна быть хотя бы небольшая логика (исчисление предикатов), чтобы увидеть универсальный или экзистенциальный квантификатор. Если вы никогда не видели исчисление предикатов или вам не нравятся квантификаторы (и я видел во время квалификационных экзаменов кандидатов, которые не устраивают), то для вас нет простого объяснения
forall
.Это квантификатор типа . Если вы еще не видели System F и не научились писать полиморфные типы, вас это
forall
смущает. Опыт работы с Haskell или ML недостаточен, потому что обычно эти языки не включаютforall
полиморфные типы. (На мой взгляд, это ошибка дизайна языка.)В частности, в Haskell
forall
используется путями, которые меня смущают. (Я не теоретик типов, но моя работа приводит меня в соприкосновение с большой теорией типов, и я вполне доволен ею.) Для меня главный источник путаницы заключается в том, чтоforall
используется для кодирования типа, который Я сам предпочел бы написать сexists
. Это оправдано хитрым изоморфизмом типов, включающим квантификаторы и стрелки, и каждый раз, когда я хочу это понять, мне приходится искать вещи и самостоятельно разрабатывать изоморфизм.Если вас не устраивает идея изоморфизма типов, или если у вас нет практики думать об изоморфизмах типов, такое использование
forall
может помешать вам.Хотя общая концепция
forall
всегда одна и та же (обязательна для введения переменной типа), детали различного использования могут значительно различаться. Неофициальный английский не очень хороший инструмент для объяснения вариаций. Чтобы действительно понять, что происходит, вам нужна математика. В этом случае соответствующую математику можно найти во вводном тексте Бенджамина Пирса « Типы и языки программирования» , который является очень хорошей книгой.Что касается ваших конкретных примеров,
runST
должен сделать вашу голову болит. Типы высшего ранга (слева от стрелки) редко встречаются в дикой природе. Я призываю вас прочитать статью, которая представляетrunST
: «Ленивые потоки функционального состояния» . Это действительно хорошая статья, и она даст вам гораздо лучшую интуицию для типаrunST
в частности и для типов более высокого ранга в целом. Объяснение занимает несколько страниц, оно очень хорошо сделано, и я не собираюсь здесь его сокращать.Рассматривать
Если я вызываю
bar
, я могу просто выбрать любой тип,a
который мне нравится, и я могу передать ему функцию от типаa
к типуa
. Например, я могу передать функцию(+1)
или функциюreverse
. Вы можете думать о том,forall
как «я могу выбрать тип сейчас». (Техническое слово для выбора типа - создание экземпляра .)Ограничения на вызов
foo
гораздо более строгие: аргументfoo
должен быть полиморфной функцией. С этим типом единственные функции, которые я могу передать,foo
этоid
или функция, которая всегда расходится, или ошибки, напримерundefined
. Причина в том, что сfoo
,forall
слева от стрелки, так что вызывающийfoo
я не могу выбрать то, чтоa
есть - скорее это реализация того,foo
что нужно, чтобы выбрать то, чтоa
есть. Поскольку онforall
находится слева от стрелки, а не над стрелкой, как вbar
, создание экземпляра происходит в теле функции, а не в месте вызова.Резюме: полное объяснение
forall
ключевого слова требует математики и могут быть поняты только тем , кто изучал математику. Даже частичные объяснения трудно понять без математики. Но, возможно, мои частичные, не математические объяснения немного помогут. Иди читай Лончбери и Пейтон ДжонсrunST
!Приложение: Жаргон «выше», «ниже», «слева от». Они не имеют ничего общего с текстовыми способами написания типов и не имеют ничего общего с деревьями абстрактного синтаксиса. В абстрактном синтаксисе a
forall
принимает имя переменной типа, а затем есть полный тип «ниже». Стрелка принимает два типа (аргумент и тип результата) и формирует новый тип (тип функции). Тип аргумента - «слева от» стрелки; это левый дочерний элемент стрелки в дереве абстрактного синтаксиса.Примеры:
В
forall a . [a] -> [a]
, поле находится над стрелкой; что слева от стрелки[a]
.В
тип в скобках будет называться "поле слева от стрелки". (Я использую такие типы в оптимизаторе, над которым я работаю.)
источник
forall a . [a] -> [a]
поле слева от стрелки.forall
в этих обстоятельствах просто пропущу как эффективную линию шум. Я посмотрю ту статью, на которую вы ссылались (спасибо за ссылку!) И посмотрю, находится ли она в моей сфере понимания. Престижность.exists
никогда не была реализована. (Это не часть Системы F!) В Haskell часть Системы F сделана неявной, ноforall
это одна из вещей, которую нельзя полностью охватить. Это как если бы они начали с Хиндли-Милнера, который позволилforall
бы стать неявным, а затем выбрали более мощную систему типов, что сбило с толку тех из нас, кто изучал «добычу» и «существование» FOL и остановился на этом.Мой оригинальный ответ:
Как указывает Норман, очень трудно дать ясное, простое английское объяснение технического термина из теории типов. Мы все пытаемся все же.
Есть только одна вещь, которую нужно помнить о «forall»: он связывает типы с некоторой областью действия . Как только вы это поймете, все будет довольно просто. Это эквивалент «лямбды» (или формы «пусть») на уровне типов - Норман Рэмси использует понятие «слева» / «выше», чтобы передать эту же концепцию объема в своем превосходном ответе .
Большинство вариантов использования «forall» очень просты, и вы можете найти их в Руководстве пользователя GHC, S7.8 ., В частности, в превосходном S7.8.5 для вложенных форм «forall».
В Haskell мы обычно оставляем связыватель для типов, когда тип определяется количественно, например:
эквивалентно:
Вот и все.
Поскольку теперь вы можете привязать переменные типа к некоторой области, у вас могут быть области, отличные от верхнего уровня (« универсально измеренный »), как в первом примере, где переменная типа видна только в структуре данных. Это позволяет скрытые типы (« экзистенциальные типы »). Или мы можем иметь произвольную вложенность привязок («ранг N типов»).
Чтобы глубоко понять системы типов, вам нужно выучить некоторый жаргон. Это природа информатики. Однако простое использование, как и выше, должно быть в состоянии понять интуитивно, по аналогии с «let» на уровне значения. Отличное введение - Лончбери и Пейтон Джонс .
источник
length :: forall a. [a] -> Int
это не эквивалентно тому,length :: [a] -> Int
когдаScopedTypeVariables
включено. Когдаforall a.
есть, это влияетlength
наwhere
предложение (если оно есть) и меняет значение именованныхa
в нем переменных типа .А как насчет простой логики первого порядка?
forall
довольно ясно в отношении универсального количественного определения , и в этом контексте термин экзистенциальный также имеет больше смысла, хотя было бы менее неловко, если бы былоexists
ключевое слово. То, является ли квантификация эффективно универсальной или экзистенциальной, зависит от расположения квантификатора относительно того, где переменные используются по какой стороне стрелки функции, и все это немного сбивает с толку.Так что, если это не помогает, или если вам просто не нравится символическая логика, с более функциональной точки зрения программирования вы можете рассматривать переменные типа как просто (неявные) параметры типа для функции. Функции, принимающие параметры типа в этом смысле, традиционно пишутся с использованием прописной лямбды по любой причине, которую я здесь напишу как
/\
.Итак, рассмотрим
id
функцию:Мы можем переписать его как лямбду, убрав «параметр типа» из сигнатуры типа и добавив встроенные аннотации типов:
Вот то же самое, что сделано для
const
:Так что ваша
bar
функция может выглядеть примерно так:Обратите внимание, что тип функции, данной
bar
в качестве аргумента, зависит отbar
параметра типа. Подумайте, было ли у вас что-то вроде этого:Вот
bar2
применяется функция к чему-то типаChar
, поэтому предоставлениеbar2
любого параметра типа, кромеChar
, приведет к ошибке типа.С другой стороны, вот что
foo
может выглядеть так:В отличие от
bar
, наfoo
самом деле не принимает никаких параметров типа вообще! Требуется функция, которая сама принимает параметр типа, а затем применяет эту функцию к двум различным типам.Поэтому, когда вы видите
forall
в сигнатуре типа, просто думайте о ней как о лямбда-выражении для сигнатур типов . Так же, как обычные лямбды, сфераforall
расширяется настолько далеко, насколько это возможно, вплоть до заключенных в скобки, и точно так же, как переменные, связанные в обычной лямбде, переменные типа, связанные с помощью a,forall
находятся только в области видимости в выражении с квантификацией.Post scriptum : Возможно, вы можете задаться вопросом - теперь, когда мы думаем о функциях, принимающих параметры типа, почему мы не можем сделать что-то более интересное с этими параметрами, чем поместить их в сигнатуру типа? Ответ в том, что мы можем!
Функция, которая помещает переменные типа вместе с меткой и возвращает новый тип, является конструктором типа , который можно написать примерно так:
Но нам понадобятся совершенно новые обозначения, потому что способ написания такого типа, как
Either a b
, уже наводит на мысль о «применении функцииEither
к этим параметрам».С другой стороны, функция, которая как бы «соответствует шаблону» по параметрам своего типа и возвращает разные значения для разных типов, является методом класса типов . Небольшое расширение моего
/\
синтаксиса выше предлагает что-то вроде этого:Лично я думаю, что предпочитаю фактический синтаксис Haskell ...
Функция, которая «сопоставляет» параметры своего типа и возвращает произвольный существующий тип, является семейством типов или функциональной зависимостью - в первом случае она даже уже во многом похожа на определение функции.
источник
λ
на то пошло, но расширение синтаксиса GHC в Юникоде не поддерживает это, потому что λ - это буква , и этот прискорбный факт гипотетически применим и к моим гипотетическим абстракциям с большим лямбда-выражением. Отсюда/\
по аналогии с\
. Я полагаю, что я мог бы просто использовать,∀
но я пытался избежать исчисления предикатов ...Вот быстрое и грязное объяснение в простых выражениях, с которым вы, вероятно, уже знакомы.
forall
Ключевое слово действительно используется только в одном пути в Haskell. Это всегда означает одно и то же, когда вы видите это.Универсальное количественное определение
Повсеместно количественно тип является типом формы
forall a. f a
. Значение этого типа можно рассматривать как функцию, которая принимает тип вa
качестве аргумента и возвращает значение типаf a
. За исключением того, что в Haskell эти аргументы типа неявно передаются системой типов. Эта «функция» должна давать вам одно и то же значение независимо от того, какой тип она получает, поэтому значение является полиморфным .Например, рассмотрим тип
forall a. [a]
. Значение этого типа принимает другой типa
и возвращает список элементов того же типаa
. Конечно, есть только одна возможная реализация. Это должно было бы дать вам пустой список, потому что этоa
может быть абсолютно любой тип. Пустой список - это единственное значение списка, которое полиморфно по своему типу элемента (так как у него нет элементов).Или тип
forall a. a -> a
. Вызывающая сторона такой функции предоставляет как тип, такa
и значение типаa
. Затем реализация должна возвращать значение того же типаa
. Есть только одна возможная реализация снова. Это должно было бы вернуть то же значение, которое было дано.Экзистенциальное количественное определение
Экзистенциально количественно типа будет иметь вид
exists a. f a
, если Хаскела поддерживает эту запись. Значение этого типа можно рассматривать как пару (или «продукт»), состоящую из типаa
и значения типаf a
.Например, если у вас есть значение типа
exists a. [a]
, у вас есть список элементов некоторого типа. Это может быть любой тип, но даже если вы не знаете, что это, вы многое можете сделать с таким списком. Вы можете изменить его, или вы можете сосчитать количество элементов, или выполнить любую другую операцию со списком, которая не зависит от типа элементов.ОК, подожди минутку. Почему Haskell использует
forall
для обозначения «экзистенциальный» тип, подобный следующему?Это может сбивать с толку, но в действительности описывает тип конструктора данных
SB
:После создания вы можете думать о значении типа
ShowBox
как о двух вещах. Это типs
вместе со значением типаs
. Другими словами, это значение экзистенциально квантифицированного типа.ShowBox
можно было бы написать такexists s. Show s => s
, как если бы Хаскелл поддерживал эту запись.runST
и друзьяУчитывая это, как они отличаются?
Давайте сначала возьмем
bar
. Он принимает типa
и функцию типаa -> a
и создает значение типа(Char, Bool)
. Мы могли бы выбратьInt
какa
и дать ему функцию типа,Int -> Int
например. Ноfoo
это другое. Это требует, чтобы реализацияfoo
была в состоянии передать любой тип, который она хочет, функции, которую мы ей даем. Таким образом, единственная функция, которую мы могли бы разумно дать, этоid
.Теперь мы должны иметь возможность разобраться со значением типа
runST
:Таким образом,
runST
должен быть в состоянии произвести значение типаa
, независимо от того, какой тип мы даемa
. Для этого он использует аргумент типа,forall s. ST s a
который, безусловно, должен как-то генерироватьa
. Более того, он должен иметь возможность генерировать значение типаa
независимо от того, какой тип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
в контексте Haskell и его систем типов.Но прежде чем вы поймете, что я хотел бы направить вас к очень доступной и приятной беседе Рунара Бьярнасона под названием « Освобождение от ограничений, ограничение свобод ». Доклад полон примеров из реальных сценариев использования, а также примеров в Scala в поддержку этого утверждения, хотя и не упоминает
forall
. Я постараюсь объяснитьforall
перспективу ниже.Очень важно переварить и поверить в это утверждение, чтобы перейти к следующему объяснению, поэтому я призываю вас посмотреть доклад (по крайней мере, его часть).
Теперь очень распространенный пример, показывающий выразительность системы типов Haskell, - это сигнатура типа:
foo :: a -> a
Говорят, что, учитывая эту сигнатуру типа, существует только одна функция, которая может удовлетворить этот тип, и это
identity
функция или то, что более широко известноid
.На начальных этапах изучения Хаскелла я всегда интересовался следующими функциями:
они оба удовлетворяют вышеуказанной сигнатуре типа, тогда почему люди из Haskell утверждают, что только она
id
удовлетворяет сигнатуре типа?Это связано с тем, что
forall
в сигнатуре типа скрыто неявное . Фактический тип:Итак, теперь давайте вернемся к утверждению: ограничения освобождают, ограничения свободы
Переводя это в систему типов, это утверждение становится:
Ограничение на уровне типа становится свободой на уровне термина
и
Свобода на уровне типа становится ограничением на уровне термина
Попробуем доказать первое утверждение:
Ограничение на уровне типа.
Таким образом, наложение ограничения на нашу подпись типа
становится свободой на уровне термина дает нам свободу или гибкость, чтобы написать все эти
То же самое можно наблюдать,
a
связывая с любым другим типом классов и т. Д.Так что теперь, что этот тип подписи:
foo :: (Num a) => a -> a
переводит в это:Это называется экзистенциальной квантификацией, что означает, что существуют некоторые экземпляры,
a
для которых функция, получающая что-то типа,a
возвращает что-то того же типа, и все эти экземпляры принадлежат множеству чисел.Следовательно, мы можем видеть добавление ограничения (которое
a
должно принадлежать набору Numbers), освобождающее термин level для нескольких возможных реализаций.Теперь перейдем ко второму утверждению и тому, которое фактически содержит объяснение
forall
:Свобода на уровне типа становится ограничением на уровне термина
Итак, теперь давайте освободим функцию на уровне типа:
Теперь это переводится как:
Это означает, что реализация этого типа подписи должна быть такой, чтобы она была
a -> a
для всех обстоятельств.Так что теперь это начинает ограничивать нас на уровне термина. Мы больше не можем писать
потому что эта реализация не будет удовлетворять, если мы поставим
a
какBool
.a
может бытьChar
или[Char]
или пользовательский тип данных. При любых обстоятельствах он должен возвращать что-то похожего типа. Эта свобода на уровне типов - это то, что известно как Универсальное Количественное определение, и единственная функция, которая может удовлетворить этокоторая обычно известна как
identity
функцияСледовательно
forall
, этоliberty
на уровне типа, фактическая цель которого состоит вconstrain
уровне термина для конкретной реализации.источник
Причина использования этого ключевого слова по-разному заключается в том, что оно фактически используется как минимум в двух разных системных расширениях: типах более высокого ранга и экзистенциалах.
Вероятно, лучше просто прочитать и понять эти две вещи по отдельности, а не пытаться получить объяснение того, почему «forall» является подходящим синтаксисом в обоих случаях одновременно.
источник
Как экзистенциально экзистенциально?
Объяснение почему
forall
вdata
определениях изоморфно(exists a. a)
(псевдо- Хаскеллу ), можно найти в викибукском «Хаскелле / Экзистенциально квантифицированные типы» .Ниже приводится краткое стенографическое резюме:
При сопоставлении с образцом / деконструкции
MkT x
, какой типx
?x
может быть любого типа (как указано вforall
), и поэтому это тип:Следовательно, следующие изоморфны:
Форалл означает Форалл
Моя простая интерпретация всего этого заключается в том, что «
forall
действительно означает« для всех »». Важное различие, которое нужно сделать, - это влияниеforall
на определение и функцию применением .А
forall
означает определение значения или функции должно быть полиморфным.Если определяемая вещь является полиморфным значением , то это означает, что значение должно быть действительным для всех подходящих
a
, что является довольно ограничительным.Если определяемая вещь является полиморфной функцией , то это означает, что функция должна быть действительной для всех подходящих
a
, что не является ограничительным, потому что только то, что функция полиморфна, не означает, что применяемый параметр должен быть полиморфным. То есть, если функция справедлива для всехa
, то, наоборот, любой подходящийa
может быть применена функция к функции. Однако тип параметра можно выбрать только один раз в определении функции.Если a
forall
находится внутри типа параметра функции (то есть aRank2Type
), то это означает, что применяемый параметр должен быть по- настоящему полиморфным, чтобы соответствовать идее определенияforall
средств, являющейся полиморфной. В этом случае тип параметра может быть выбран более одного раза в определении функции ( «и выбирается реализацией функции», как указано Норманом )Следовательно, причина, по которой экзистенциальные
data
определения допускают любое,a
состоит в том, что конструктор данных является полиморфной функцией :вид MkT ::
a -> *
Что означает, что любой
a
может быть применен к функции. В противоположность, скажем, полиморфному значению :вид значения T ::
a
Это означает, что определение valueT должно быть полиморфным. В этом случае
valueT
может быть определен пустой список[]
всех типов.Различия
Несмотря на то, что значение для
forall
является постоянным вExistentialQuantification
иRankNType
, у existentials есть различие, так какdata
конструктор может использоваться в сопоставлении с образцом. Как описано в руководстве пользователя ghc :источник