Ваша проблема не имеет ничего общего с Floating
, а связана с тем, что вы хотите составить функцию с двумя аргументами и функцию с одним аргументом без проверки типов. Я приведу вам пример составной функции reverse . foldr (:) []
.
reverse . foldr (:) []
имеет тип [a] -> [a]
и работает должным образом: возвращает перевернутый список (foldr (:) []
по сути _ 6_ для списков).
Однако 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:
Читайте также:
person
Mirzhan Irkegulov
schedule
02.09.2015