Моя предыстория: я действительно не понимаю вообще или когда Haskell имеет их неявно.
Хорошо, рассмотрим тип id
, a -> a
. Что означает a
и откуда оно взялось? Когда вы определяете значение, вы не можете просто использовать произвольные переменные, которые нигде не определены. Вам нужно определение верхнего уровня, или аргумент функции, или предложение where
, и т. д. В общем, если вы используете переменную, она должна быть где-то привязана.
То же самое относится и к переменным типа, и forall
— один из таких способов связать переменную типа. Везде, где вы видите переменную типа, которая не связана явно (например, class Foo a where ...
связывает a
внутри определения класса), она неявно связана forall
.
Таким образом, тип id
неявно равен forall a. a -> a
. Что это значит? Почти то, что он говорит. Мы можем получить тип a -> a
для всех возможных типов a
, или с другой точки зрения, если вы выберете какой-либо конкретный тип, вы можете получить тип, представляющий "функции из выбранного вами типа самому себе". Последняя формулировка должна звучать как определение функции, и поэтому вы можете думать о forall
как о лямбда-абстракции для типов.
GHC использует различные промежуточные представления во время компиляции, и одно из применяемых преобразований делает сходство с функциями более прямым: неявные forall
становятся явными, и везде, где полиморфное значение используется для определенного типа, оно сначала применяется к аргумент типа.
Мы даже можем записать как forall
s, так и лямбды как одно выражение. Я на мгновение злоупотреблю обозначениями и заменю forall a.
на /\a =>
для визуальной согласованности. В этом стиле мы можем определить id = /\a => \(x::a) -> (x::a)
или что-то подобное. Таким образом, такое выражение, как id True
в вашем коде, в конечном итоге будет преобразовано во что-то вроде id Bool True
; просто id True
уже не имело бы смысла.
Точно так же, как вы можете переупорядочивать аргументы функции, вы можете переупорядочивать и аргументы типа, но только с тем (довольно очевидным) ограничением, что аргументы типа должны стоять перед любыми аргументами значения этого типа. Поскольку неявные forall
всегда являются самым внешним уровнем, GHC потенциально может выбирать любой порядок, который он хочет, делая их явными. В обычных условиях это, очевидно, не имеет значения.
Я не уверен, точно что происходит в этом случае, но, основываясь на комментариях, я бы предположил, что преобразование в использование явных аргументов типа и дешугаризация нотации do
в некотором смысле не осведомлены друг от друга, поэтому порядок аргументов типа указывается явно для обеспечения согласованности. В конце концов, если что-то слепо применяет два аргумента типа к выражению, очень важно, является ли тип этого выражения forall a b. m a -> m b -> m b
или forall b a. m a -> m b -> m b
!
person
C. A. McCann
schedule
20.09.2012
forall
вообще делает, или что такое типы более высокого ранга. Вопрос в том, что именноforall
делает в этом коде, в свете следующего за ним комментария. - person Matt Fenwick   schedule 21.09.2012