Это не значит, что у вас нет доступа к нему, это означает, что экземпляр контроля над типом обращается вспять: используя тип ранга 2, вы меняете того, кто выбирает тип.
В вашем примере функция test2
получает выбор своего аргумента. Так, например, это составляет:
test2 :: (forall x. Show x => x) -> String
test2 a = show (a :: Char)
Это происходит потому, что аргумент a
должны буквально работать для всех типов, которые являются экземпляром Show
. В результате мы никогда не можем позвонить test2
, так как мы никогда не сможем предоставить значение, которое может принимать все возможные типы, которые являются экземплярами Show
.
Обратите внимание, что если бы мы написали
test2 :: Show x => x -> String
test2 a = ...
абонент из test2
получает, чтобы выбрать тип a
и test2
является тот, который должен работать для всех экземпляров Show
.
Давайте рассмотрим немного более полезный пример:
example :: (forall x. Show x => x -> Int) -> Int
example f = 10 * f True
Мы можем видеть, что, опять же, мы (то есть, example
) получите, чтобы выбрать тип представленного переменной в x
типа (это конкретизирует этот тип переменной относится к определенному типу). Здесь мы выбираем тип Bool
. Вот как вы могли бы назвать эту функцию:
ghci> example (length . show)
40
Вот еще один пример:
example2 :: (forall x. [x] -> [x]) -> [Int]
example2 f = f [1, 2, 3]
Мы можем дать ему какую-либо функции в списках , пока что функция не может заботиться о фактических значениях в том, что список.Поскольку функция не знает тип этих значений, она не может их каким-либо образом проверять (это свойство называется parametricity).
Таким образом, мы можем сделать что-то вроде этого:
ghci> example2 reverse
[3,2,1]
... но не что-то вроде этого:
ghci> let addOne :: Int -> Int
|> addOne a = a + 1
|>
ghci> example2 (map addOne)
<interactive>:33:15:
Couldn't match type ‘x’ with ‘Int’
‘x’ is a rigid type variable bound by
a type expected by the context: [x] -> [x] at <interactive>:33:1
Expected type: x -> x
Actual type: Int -> Int
In the first argument of ‘map’, namely ‘addOne’
In the first argument of ‘example2’, namely ‘(map addOne)’
Насколько практической полезности идет, ранга п типа используются широко в (например) библиотеку lens
. Это позволяет вам выразить мысль о том, что функция использует экземпляр Functor
, не зная, какой конкретный экземпляр Functor
(то есть он просто использует на нем fmap
). Из-за этого, когда вы его передаете, вы можете дать ему любой нужный вам пример Functor
.
Извините, я считаю, что неправильно сформулировал свой вопрос, и теперь я переформулировал его. Я понимаю более высокие типы рангов формы для 'test2' в моем первоначальном примере. Но вы не обращали внимание на то, как работает тип 'test1', где и находится моя путаница. –
@MikeIzbicki Я почти уверен, что в 'test1' на самом деле есть неявный' forall x.' внутри круглых скобок, затеняя исходный 'x' и делая его эквивалентным' test2'. Обратите внимание, что '(Показать x => x) -> String' (без явного' forall') имеет такое же поведение, а также требует 'RankNTypes'. –
Это не может быть так, потому что 'test1 a = show (a :: Char)' не компилируется. –