Ответ 1
TL; DR: Тип переменной порядок определяется первой встречей слева направо. В случае сомнений используйте :type +v
.
Не использовать :type
Использование :type
вводит в заблуждение. :type
выводит тип целого выражения. Поэтому, когда вы пишете :t (,)
, проверяющий тип смотрит на
(,) :: forall a b. a -> b -> (a, b)
и создает экземпляры всех переменных с новыми переменными типа
(,) :: a1 -> b1 -> (a1, b1)
который необходим, если вы будете применять (,)
. Увы, вы не делаете этого, поэтому вывод типа почти завершен, и он обобщает все свободные переменные, и вы получаете, например,
(,) :: forall {b} {a}. a -> b -> (a, b)
Этот шаг не дает никаких гарантий над порядком свободной переменной, и компилятор может свободно меняться.
Также обратите внимание, что он пишет forall {a}
вместо forall a
, что означает, что вы не можете использовать приложение видимого типа здесь.
Использование :type +v
Но, конечно, вы можете использовать (,) @Bool
но здесь тип-checker рассматривает первое выражение по-разному и не выполняет этот этап создания/обобщения.
И вы можете получить это поведение в GHCi, а также - передать +v
на :type
:
:type +v (,)
(,) :: forall a b. a -> b -> (a, b)
:type +v (,) @Bool
(,) @Bool :: forall b. Bool -> b -> (Bool, b)
Посмотрите, нет {…}
вокруг переменных типа!
Откуда этот порядок?
Раздел руководства пользователя GHC о приложении видимого типа:
Если сигнатура типа идентификатора не содержит явного forall, аргументы переменной типа отображаются в порядке слева направо, в котором переменные появляются в типе. Таким образом, foo :: Monad m => ab → m (ac) будет иметь свои типовые переменные, упорядоченные как m, a, b, c.
Это относится только к вещам, которые имеют явную подпись типа. Нет гарантированного порядка для переменных в предполагаемых типах, но вы также не можете использовать выражения с выведенными типами с помощью VisibleTypeApplication
.