Ну, здесь есть три функции:
(.) :: (b -> c) -> (a -> b) -> a -> c
;
flip :: (a -> b -> c) -> b -> a -> c
; и
const :: a -> b -> a
.
Обратите внимание, что если вы используете функцию (.)
в качестве оператора, вы на самом деле написали:
(.) flip const
или более многословный:
((.) flip) const
Теперь давайте сначала напишем подписи функций в подробном виде и с разными именами, которые не будут конфликтовать:
(.) :: (b -> c) -> ((a -> b) -> (a -> c))
flip :: (d -> (e -> f)) -> (e -> (d -> f))
const :: g -> (h -> g)
Таким образом, мы применяем (.)
к flip
, что означает, что мы должны сопоставить параметр (.)
с типом (b -> c)
с сигнатурой flip, поэтому мы решаем это с помощью:
b -> c
(d -> (e -> f)) -> (e -> (d -> f))
Какое единственное возможное совпадение (обратите внимание на скобки). Это означает, что:
b ~ (d -> (e -> f))
c ~ (e -> (d -> f))
(здесь a ~ b
означает, что a
и b
относятся к одному типу)
В результате тип (.) flip
равен
(.) flip :: (a -> b) -> (a -> c)
Это опять-таки функция с одним параметром (все функции в Haskell имеют один параметр), и этот параметр имеет тип a -> b
.
и мы применяем эту функцию к const
, поэтому мы снова делаем сопоставление с шаблоном:
a -> b
g -> (h -> g)
так что это означает, что a ~ g
и b ~ (d -> (e -> f)) ~ (h -> g)
, в результате мы знаем, что d ~ h
и g ~ (e -> f)
.
Мы знаем, что тип ((.) flip) const
имеет тип:
((.) flip) const :: a -> c`
Так что теперь нужно заменить: a
на g
и g ~ (e -> f)
, поэтому a ~ (e -> f)
. Кроме того, мы знаем, что c ~ (e -> (d -> f))
, так что это означает, что тип:
((.) flip) const :: (e -> f) -> (e -> (d -> f))
или в менее подробной форме:
flip . const :: (e -> f) -> e -> d -> f
, что, за исключением переименования переменных, совпадает с типом, полученным из GHCi.