Вы упускаете, что вы можете ограничить область переменной количественному типа:
modifyPair :: (Num b, Num c) => (forall a. Num a => a -> a) -> (b, c) -> (b, c)
modifyPair f (b, c) = (f b, f c)
Попробуйте записать эту функцию без расширения RankNTypes
. В частности, позволяя элементам пары быть разными типами друг от друга.
Этот пример пример не слишком полезен, но общая идея имеет смысл. Вы можете указать, что аргумент функции должен быть полиморфным.
Существует дополнительный трюк, который вы можете выполнить с помощью этой оснастки. Канонический пример исходит от ST
. Цель библиотеки ST
- разрешить ограниченное использование изменяемых данных. То есть вы можете реализовать алгоритм, включающий истинную мутацию, и представить внешний интерфейс, который является чистым. ST
сами заботятся доказательства того, что использование безопасно с помощью уловок системы умного типа:
newtype ST s a = ... -- details aren't important
newtype STRef s a = ... -- details still aren't important
-- a standard-ish interface to mutable data
newSTRef :: ST s (STRef s a)
readSTRef :: STRef s a -> ST s a
writeSTRef :: STRef s a -> a -> ST s()
-- the magic using RankNTypes
runST :: (forall s. ST s a) -> a
Это дополнительный типа переменного, s
, стоит обратить внимание. Он отображается как в ST
, так и в STRef
. Каждая функция, которая управляет STRef
, гарантирует, что s
в ST
и STRef
является той же переменной. Поэтому, когда вы доберетесь до runST
, вы обнаружите, что тип s
не имеет отношения к типу a
. s
имеет более ограниченную область, чем у a
. Конечным результатом этого является то, что вы не можете написать что-то вроде runST newSTRef
. Средство проверки типов отклонит его, потому что переменная типа s
должна будет избежать контекста, в котором она определена.
Итак, действительно есть некоторые полезные трюки, которые вы можете вытащить, когда вы можете указать, что аргумент функции должен быть полиморфным.
В вашем конкретном примере фактически не используется тип более высокого ранга. Это используется только с расширением 'RankNTypes', чтобы вы могли использовать явные' forall' кванторы в сигнатуре типа. Существует еще одно расширение 'ExplicitForAll', которое позволяет вам использовать' forall', не включая никаких новых функций. – Ben
@Ben, есть также расширение ScopedTypeVariables, которое фактически делает их полезными. – dfeuer
@dfeuer Да, я считаю, что именно поэтому существует '' Явный ForAll''. Универсальная квантификация ('forall') всегда существовала как * подразумеваемая * концепция.Таким образом, явное указание на них - это «расширение», которое само по себе имеет смысл независимо от любого другого расширения, даже если оно не позволяет вам ** делать что-либо по-другому по своему усмотрению. Поскольку для расширений множественного типа требуется явное 'forall', наличие отдельного расширения только для синтаксиса forall' позволяет другим расширениям зависеть от него, а не зависящим друг от друга или дублирующим функциональность. – Ben