Ваша проблема не имеет ничего общего с Floating
, но с тем фактом, что вы хотите составить функцию с двумя аргументами и функцию с одним аргументом так, чтобы она не проверялась. Я дам вам пример с точки зрения составной функции reverse . foldr (:) []
.
reverse . foldr (:) []
имеет тип [a] -> [a]
и работает как ожидалось: он возвращает перевернутый список (foldr (:) []
по существу id
для списков).
Однако reverse . foldr (:)
не проверяет тип. Почему?
Когда типы соответствуют составу функций
Давайте рассмотрим несколько типов:
reverse :: [a] -> [a]
foldr (:) :: [a] -> [a] -> [a]
foldr (:) [] :: [a] -> [a]
(.) :: (b -> c) -> (a -> b) -> a -> c
reverse . foldr (:) []
проверки типов, потому что (.)
создает экземпляр для:
(.) :: ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
Другими словами, в аннотации типа для (.)
:
a
становится [a]
b
становится [a]
c
становится [a]
Итак, reverse . foldr (:) []
имеет тип [a] -> [a]
.
Когда типы не соответствуют составу функций
reverse . foldr (:)
не проверяет тип, потому что:
foldr (:) :: [a] -> [a] -> [a]
Будучи правым оперантом (.)
, он будет реализовывать тип от a -> b
до [a] -> ([a] -> [a])
. То есть в:
(b -> c) -> (a -> b) -> a -> c
- Тип переменной
a
будет заменен на [a]
- Переменная типа
b
будет заменена на [a] -> [a]
.
Если тип foldr (:)
был a -> b
, тип (. foldr (:))
будет:
(b -> c) -> a -> c`
(foldr (:)
применяется в качестве правого операнда для (.)
).
Но поскольку тип foldr (:)
равен [a] -> ([a] -> [a])
, тип (. foldr (:))
равен:
(([a] -> [a]) -> c) -> [a] -> c
reverse . foldr (:)
не проверяет тип, потому что reverse
имеет тип [a] -> [a]
, а не ([a] -> [a]) -> c
!
Сова оператор
Когда люди впервые изучают композицию функций в Haskell, они узнают, что, когда у вас есть последний аргумент функции в самой правой части тела функции, вы можете удалить его как из аргументов, так и из тела, заменив или заключив в скобки (или знаки доллара) с точками. Другими словами, следующие 4 определения функции эквивалентны :
f a x xs = g ( h a ( i x xs))
f a x xs = g $ h a $ i x xs
f a x xs = g . h a . i x $ xs
f a x = g . h a . i x
Таким образом, люди получают интуицию, которая говорит: «Я просто удаляю самую правую локальную переменную из тела и аргументов», но эта интуиция ошибочна, потому что, как только вы удалили xs
,
f a x = g . h a . i x
f a = g . h a . i
не эквивалентно ! Вы должны понимать, когда проверки типов композиции функций, а когда нет. Если вышеуказанные 2 были эквивалентны, то это означало бы, что приведенные ниже 2 также эквивалентны:
f a x xs = g . h a . i x $ xs
f a x xs = g . h a . i $ x xs
, что не имеет смысла, потому что x
не является функцией с xs
в качестве параметра. x
является параметром для функции i
, а xs
является параметром для функции (i x)
.
Есть способ сделать функцию с 2 параметрами бессмысленными. А это значит использовать оператор «сова»:
f a x xs = g . h a . i x xs
f a = g . h a .: i
where (.:) = (.).(.)
Приведенные выше два определения функций эквивалентны. Читайте подробнее об операторе «Сова» .
Ссылки
Программирование на Haskell становится намного проще и понятнее, когда вы понимаете функции, типы, частичное применение и каррирование, составление функций и оператор доллара. Чтобы понять эти понятия, прочитайте следующие ответы StackOverflow:
Читайте также: