Почему этот код Haskell компилируется? - программирование
Подтвердить что ты не робот

Почему этот код Haskell компилируется?

Учитывая:

uncurry :: (a-> b -> c) -> (a,b) -> c    
id :: a -> a

Вызов uncurry id приводит к функции типа: (b -> c, b) -> c

Как мы получаем этот результат?

Как вы можете использовать id (a → a) в качестве первого параметра для нечеткой, для чего требуется функция (a → b → c)?

4b9b3361

Ответ 1

Это проще понять, если мы попытаемся посмотреть на него с точки зрения создания типов: выясним, что нам нужно сделать для типа id, чтобы он соответствовал форме, требуемой uncurry. Поскольку мы имеем:

id :: a -> a

мы также имеем:

id :: (b -> c) -> (b -> c)

Это можно увидеть, заменив b -> c на a в исходном типе id, так же, как вы можете заменить Int при определении типа id 42. Затем мы можем оставить круглые скобки в правой части, так как (->) является право-ассоциативным:

id :: (b -> c) -> b -> c

показывающий, что тип id соответствует форме a -> b -> c, где a - b -> c. Другими словами, мы можем изменить тип id, чтобы он соответствовал требуемой форме, просто специализировав общий тип, который он уже имеет.

Еще один способ понять это - увидеть, что uncurry ($) также имеет тип (b -> c, b) -> c. Сравнивая определения id и ($):

id :: a -> a
id a = a

($) :: (a -> b) -> a -> b
($) f x = f x

мы можем сделать последнее определение более бессмысленным:

($) f = f

в этот момент становится понятным тот факт, что ($) является просто специализацией id более конкретному типу.

Ответ 2

Как вы можете использовать id (a → a) в качестве первого параметра для нечеткой, для чего требуется функция (a → b → c)?

На самом деле uncurry требуется функция (a -> (b -> c)). Вы можете заметить разницу?:)

Опуская круглые скобки - это зло (ну, иногда). Это делает невозможным для начинающего расшифровать Haskell. Конечно, после того, как вы собрали некоторый опыт работы с языком, вы чувствуете, что больше не нуждаетесь в них.

Здесь все становится ясно, как только мы выпишем все опущенные круглые скобки явно:

uncurry :: (a -> (b -> c)) -> ((a,b) -> c)
id      ::  a ->    a

Теперь запись uncurry id вызывает объединение типов a1 -> a1 с a2 -> (b -> c). Это просто, a1 ~ a2 и a1 ~ (b -> c). Просто механический материал, нет творческого мышления. Таким образом, id имеет тип a -> a where a ~ (b -> c), поэтому uncurry id имеет тип (b -> c,b) -> c, путем простой подстановки a ~ (b -> c) в (a,b) -> c. То есть, он ожидает пару функции b -> c и значение b и должен иметь значение c.

Поскольку типы являются наиболее общими (т.е. о них ничего не известно, и поэтому нет особых функций для вызова, которые могли бы сделать трюк каким-то особым образом), единственный способ получить значение c здесь - вызвать b -> c с значением b в качестве аргумента. Естественно, что делает ($). Итак, uncurry id == uncurry ($), хотя id, безусловно, не ($).