Существуют универсально квантифицированные переменные типа, и существуют типы данных с количественной оценкой по существу. Однако, несмотря на то, что люди дают псевдокод формы exists a. Int -> a
, чтобы иногда помогать объяснять понятия, это не похоже на расширение компилятора, в котором есть какой-то реальный интерес. Это просто "нет большого значения при добавлении этого", (потому что это кажется мне ценным), или есть проблема, такая как неразрешимость, которая делает ее поистине невозможной.
EDIT: Я отметил правильный ответ как правильный, потому что похоже, что это, вероятно, фактическая причина, почему это не было включено. Я хотел бы добавить дополнительные комментарии, хотя на всякий случай кто-то захочет помочь в этом подробнее.
Как указано в комментариях, я приведу пример того, почему я считаю это полезным. Предположим, что у нас есть тип данных следующим образом:
data Person a = Person
{ age: Int
, height: Double
, weight: Int
, name: a
}
Итак, мы выбираем параметрize over a
, который является соглашением об именах (я знаю, что в этом примере, вероятно, имеет смысл сделать ADT NamingConvention
с соответствующими конструкторами данных для американской "первой, средней, последней", "испаноязычное" имя, отцовское имя, материнское имя и т.д. Но пока, просто пойдите с этим).
Итак, есть несколько функций, которые мы видим, в основном игнорируем тип, с которым параметризуется Person. Примеры:
age :: Person a -> Int
height :: Person a -> Double
weight :: Person a -> Int
И любая функция, построенная поверх них, аналогичным образом игнорирует тип a
. Например:
atRiskForDiabetes :: Person a -> Bool
atRiskForDiabetes p = age p + weight p > 200
--Clearly, I am not actually a doctor
Теперь, если у нас есть гетерогенный список людей (типа [exists a. Person a]
), мы хотели бы иметь возможность отображать некоторые из наших функций над списком. Конечно, есть некоторые бесполезные способы отображения:
heteroList :: [exists a. Person a]
heteroList = [Person 20 30.0 170 "Bob Jones", Person 50 32.0 140 3451115332]
extractedNames = map name heteroList
В этом примере extractedNames
, конечно, бесполезен, поскольку имеет тип [exists a. a]
. Однако, если мы используем другие наши функции:
totalWeight :: [exists a. Person a] -> Int
totalWeight = sum . map age
numberAtRisk :: [exists a. Person a] -> Int
numberAtRisk = length . filter id . map atRiskForDiabetes
Теперь у нас есть что-то полезное, которое работает над гетерогенной коллекцией (И мы даже не включили классные классы). Обратите внимание, что мы смогли повторно использовать существующие функции. Использование экзистенциального типа данных будет выглядеть следующим образом:
data SomePerson = forall a. SomePerson (Person a) --fixed, thanks viorior
Но теперь, как мы можем использовать age
и atRiskForDiabetes
? Мы не можем. Я думаю, что вам нужно будет сделать что-то вроде этого:
someAge :: SomePerson -> Int
someAge (SomePerson p) = age p
Который действительно хромой, потому что вам нужно переписать все ваши комбинаторы для нового типа. Это становится еще хуже, если вы хотите сделать это с типом данных, параметризованным по нескольким переменным типа. Представьте себе следующее:
somewhatHeteroPipeList :: forall a b. [exists c d. Pipe a b c d]
Я не буду объяснять эту мысль дальше, но просто обратите внимание, что вы переписываете много комбинаторов, чтобы сделать что-то подобное, используя только экзистенциальные типы данных.
Сказав это, я надеюсь, что я мягко убеждаю, что это может быть полезно. Если это не кажется полезным (или если пример кажется слишком изобретенным), не стесняйтесь, дайте мне знать. Кроме того, поскольку я в первую очередь программист и не имею подготовки по теории типов, мне немного сложно понять, как использовать теорему Сколема (как размещено viorior) здесь. Если бы кто-нибудь мог показать мне, как применить его к примеру Person a
, который я дал, я был бы очень благодарен. Спасибо.