Pull to refresh

Немного о каррировании в Haskell

Reading time2 min
Views15K
Читая М. Липовача «Изучай Haskell во имя добра!», я поначалу не понимал, чем частичное применение отличается от каррирования. Потратил некоторое время на разбор данного вопроса и набросал себе «шпаргалку» по обозначенной теме.

В 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 + 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"
Tags:
Hubs:
+17
Comments23

Articles