Читая М. Липовача «Изучай Haskell во имя добра!», я поначалу не понимал, чем частичное применение отличается от каррирования. Потратил некоторое время на разбор данного вопроса и набросал себе «шпаргалку» по обозначенной теме.
В Haskell функции без параметров называются определениями (definition) или именами (name).
Функции не могут принимать более одного параметра. Если функция, принимает несколько параметров, то на самом деле она является функцией с одним параметром и возвращает другую функцию, которая так же принимает лишь один параметр и возвращает некоторый результат (др. функцию или конкретное значение).Т.е. если для функции указана такая сигнатура:
то Haskell воспринимает её следующим образом:
Т.е. функция func принимает параметр типа Int и возвращает новую функцию, которая принимает очередной параметр — типа Double и возвращает другую новую функцию, принимающую параметр типа Char и возвращающую значение типа Bool.
Преобразование функции от многих аргументов в функцию, берущую свои аргументы по одному называется каррированием. Haskell автоматически выполняет каррирование всех функций, принимающих более одного параметра. Именно благодаря каррированию становится возможным частичное применение функций, а так же создание сечений. В свою очередь, частичное применение делает возможным существование бесточечной нотации.
Частичное применение:
Сечения:
Бесточечная нотация:
ghci:
В стандартном модуле Data.Tuple определены, помимо прочего, следующие функции:
Функция curry преобразовывает некаррированную функцию в каррированную.
Функция uncurry преобразовывает каррированную функцию в некаррированную.
В Haskell функции без параметров называются определениями (definition) или именами (name).
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 + dghci
Частичное применение:
λ: 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' = oddghci:
λ: 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 * 2ghci
λ: 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"