В математике композиция функций определяется следующим образом:
(f>°g)(x) = f (g (x))
Это значит, что композиция двух функций создаёт новую функцию, которая, когда её вызывают, скажем, с параметром x, эквивалентна вызову g с параметром x, а затем вызову f с результатом первого вызова в качестве своего параметра.
В языке Haskell композиция функций понимается точно так же. Мы создаём её при помощи оператора >(.)
, который определён следующим образом:
>(.) :: (b –> c) –> (a –> b) –> a –> c
>f . g = \x –> f (g x)
По декларации типа функция >f
должна принимать параметр того же типа, что и результат функции >g
. Таким образом, результирующая функция принимает параметр того же типа, что и функция >g
, и возвращает значение того же типа, что и функция >f
. Выражение >negate . (* 3)
возвращает функцию, которая принимает число, умножает его на три и меняет его знак на противоположный.
Одно из применений композиции функций – это создание функций «на лету» для передачи их другим функциям в качестве параметров. Конечно, мы можем использовать для этого анонимные функции, но зачастую композиция функций понятнее и лаконичнее. Допустим, что у нас есть список чисел и мы хотим сделать их отрицательными. Один из способов сделать это – получить абсолютное значение числа (модуль), а затем перевести его в отрицательное, вот так:
>ghci> map (\x –> negate (abs x)) [5,–3,–6,7,–3,2,–19,24]
>[–5,–3,–6,–7,–3,–2,–19,–24]
Обратите внимание на анонимную функцию и на то, как она похожа на результирующую композицию функций. А вот что выйдет, если мы воспользуемся композицией:
>ghci> map (negate . abs) [5,–3,–6,7,–3,2,–19,24]
>[–5,–3,–6,–7,–3,–2,–19,–24]
Невероятно! Композиция функций правоассоциативна, поэтому у нас есть возможность включать в неё много функций за один раз. Выражение >f (g (z x))
эквивалентно >(f . g . z) x
. Учитывая это, мы можем превратить
>ghci> map (\xs –> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]