Читая М. Липовача «Изучай Haskell во имя добра!», я поначалу не понимал, чем частичное применение отличается от каррирования. Потратил некоторое время на разбор данного вопроса и набросал себе «шпаргалку» по обозначенной теме.
В Haskell функции без параметров называются константными функциями, поскольку каждая из них всегда возвращает одно и то же значение.
func :: String
func = "Haskell"
Функции не могут принимать более одного параметра. Если функция, принимает несколько параметров, то на самом деле она является функцией с одним параметром и возвращает другую функцию, которая так же принимает лишь один параметр и возвращает некоторый результат (др. функцию или конкретное значение).Т.е. если для функции указана такая сигнатура:
func :: Int -> Double -> Char -> Bool
то Haskell воспринимает её следующим образом:
func :: Int -> (Double -> (Char -> Bool))
Т.е. функция func принимает параметр типа Int и возвращает новую функцию, которая принимает очередной параметр — типа Double и возвращает другую новую функцию, принимающую параметр типа Char и возвращающую значение типа Bool.
Преобразование функции от многих аргументов в функцию, берущую свои аргументы по одному называется каррированием. Haskell автоматически выполняет каррирование всех функций, принимающих более одного параметра. Именно благодаря каррированию становится возможным частичное применение функций, а так же создание сечений. В свою очередь, частичное применение делает возможным существование бесточечной нотации.
Примечание
В Haskell не существует такого понятия, как частичное применение функции. Существует применение функции (без «частично»). Если мы говорим (для удобства восприятия), что функция
f :: Int -> Int -> Int
имеет два аргумента, (что с технической точки зрения не является корректным), то мы можем так же сказать (снова для удобства восприятия), чтоf 5
— это частично применённая функция (что так же не будет корректно технически).
Пример
func :: (Num a) => a -> a -> a -> a
func a b c d = a + b + c + d
ghci
Частичное применение:
λ: let n = func 2 3
λ: let m = n 10
λ: let g = m 7
λ: g
22
Сечения:
λ: let a = (/2)
λ: a 10
5.0
λ: let b = (15/)
λ: b 5
3.0
Бесточечная нотация:
odd' :: Integral a => a -> Bool
odd' = odd
ghci:
λ: odd' 5
True
λ: odd' 4
False
Каррирование и декаррирование
В стандартном модуле Data.Tuple определены, помимо прочего, следующие функции:
curry :: ((a, b) -> c) -> a -> b -> c
uncurry :: (a -> b -> c) -> (a, b) -> c
Функция curry преобразовывает некаррированную функцию в каррированную.
Функция uncurry преобразовывает каррированную функцию в некаррированную.
Пример
msg :: Int -> Bool -> String
msg n True = show $ n `div` 2
msg n _ = show $ n * 2
ghci
λ: let n = msg
λ: let m = uncurry n
λ: :t n
n :: Int -> Bool -> String
λ: :t m
m :: (Int, Bool) -> String
λ: n 5 True
"2"
λ: m (5,True)
"2"
λ: let k = curry m
λ: :t k
k :: Int -> Bool -> String
λ: k 5 True
"2"
Автор: Hwd