Во-первых, ваш пример даже не зашёл так далеко от меня для текущего GHC, потому что вам также необходимо включить ImpredecativeTypes
. Это приводит к предупреждению о том, что ImpredicativeTypes будут упрощены или удалены в следующем GHC. Так что мы не на хорошей территории. Тем не менее, добавление правильного ограничения Num (foo :: [forall a. Num a => a]
) позволяет компилировать ваш пример.
Давайте оставим в стороне нецензурные типы и рассмотрим более простой пример:
data Foo = Foo (forall a. a)
foo = Foo 1
Это также не компилируется с ошибкой Could not deduce (Num a) from the context ()
.
Почему? Ну, тип обещает, что вы дадите конструктору Foo что-то такое же качество, что для любого типа a
, он выдаст a
. Единственное, что удовлетворяет этому, это дно. С другой стороны, целочисленный литерал обещает, что для любого типа a
класса Num он выдает a
. Так что типы явно несовместимы. Тем не менее, мы можем потянуть за собой немного дальше, чтобы получить то, что вы, вероятно, хотите:
data Foo = forall a. Foo a
foo = Foo 1
Итак, это компилируется. Но что мы можем с этим сделать? Что ж, давайте попробуем определить функцию экстрактора:
unFoo (Foo x) = x
Oops! Quantified type variable 'a' escapes
. Таким образом, мы можем определить это, но мы не можем сделать много интересного с этим. Если бы мы дали контекст класса, то мы могли бы по крайней мере использовать некоторые функции класса в нем.
Есть время и место для экзистенциалов, в том числе без контекста класса, но это довольно редко, особенно когда вы только начинаете. Когда вы в конечном итоге используете их, часто это происходит в контексте GADT, которые являются надмножеством экзистенциальных типов, но способ, которым возникают экзистенциалы, кажется вполне естественным.