Как GHCi выбирает имена переменных типа?

При использовании интерактивного интерпретатора GHC можно запросить предполагаемый тип выражения:

Prelude> :t map
map :: (a -> b) -> [a] -> [b]

Кажется, что он принимает имена переменных типа из подписи, поскольку map определяется как

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

в прелюдии. Это имеет большой смысл! Мой вопрос: как имена переменных типа выбраны, когда нет подписи?

Примером может быть

Prelude> :t map fst
map fst :: [(b, b1)] -> [b]

где он выбрал имена b и b1. Ясно, что переименование должно происходить, но просто начиная с a, b,... дал бы

map fst :: [(a, b)] -> [a]

который я считаю немного читабельнее.

Ответ 1

Как я понимаю, ghci выбирает имена в том же порядке, что и типы. Он использует схему именования, как вы упомянули, для определения имени типа результата, который является [b], потому что это имя типа, указанное в определении map. Затем он решает, что функция, которая является первым параметром map, должна также возвращать что-то типа b.

Остальная переменная типа, которая должна быть названа, является, таким образом, переменной типа для второго элемента в кортеле аргументов в fst, и, опять же, он определяет определение fst, чтобы решить, какое имя использовать. Определение fst :: (a, b) -> a, поэтому b будет предпочтительным именем здесь, но поскольку b уже занято, он добавляет 1, чтобы он стал b1.

Я думаю, что эта система имеет преимущества в ситуациях, когда вы не имеете дело с произвольными типами, как это имеет место здесь. Если результирующий тип выглядит примерно так, например:

castAdd :: (Num n, Num n1, Num n2) => n -> n1 -> n2

... это, возможно, более читаемо, чем:

castAdd :: (Num a, Num b, Num c) => a -> b -> c

... потому что вы можете в основном полагаться на то, что n# означает числовой тип, поскольку определение класса для Num равно class Num n where ....

EDIT: Да, я знаю, что castAdd невозможно реализовать, но это просто пример типа.