. Если помните, она принимает два параметра и возвращает максимальный из них. Если сделать вызов
>max 4 5
, то вначале будет создана функция, которая принимает один параметр и возвращает
>4
или поданный на вход параметр – смотря что больше. Затем значение
>5
передаётся в эту новую функцию, и мы получаем желаемый результат. В итоге оказывается, что следующие два вызова эквивалентны:
>ghci> max 4 5
>5
>ghci> (max 4) 5
>5
Чтобы понять, как это работает, давайте посмотрим на тип функции >max
:
>ghci> :t max
>max :: (Ord a) => a –> a –> a
То же самое можно записать иначе:
>max :: (Ord a) => a –> (a –> a)
Прочитать запись можно так: функция >max
принимает параметр типа >a
и возвращает (>–>
) функцию, которая принимает параметр типа >a
и возвращает значение типа >a
. Вот почему возвращаемый функцией тип и параметры функции просто разделяются стрелками.
Ну и чем это выгодно для нас? Проще говоря, если мы вызываем функцию и передаём ей не все параметры, то в результате получаем новую функцию, а именно – результат частичного применения исходной функции. Новая функция принимает столько параметров, сколько мы не использовали при вызове оригинальной функции. Частичное применение (или, если угодно, вызов функции не со всеми параметрами) – это изящный способ создания новых функций «на лету»: мы можем передать их другой функции или передать им ещё какие-нибудь параметры.
Посмотрим на эту простую функцию:
>multThree :: Int -> Int -> Int -> Int
>multThree x y z = x * y * z
Что происходит, если мы вызываем >multThree 3 5 9
или >((multThree 3) 5) 9
? Сначала значение >3
применяется к >multThree
, так как они разделены пробелом. Это создаёт функцию, которая принимает один параметр и возвращает новую функцию, умножающую на >3
. Затем значение >5
применяется к новой функции, что даёт функцию, которая примет параметр и умножит его уже на >15
. Значение >9
применяется к этой функции, и получается результат >135
. Вы можете думать о функциях как о маленьких фабриках, которые берут какие-то материалы и что-то производят. Пользуясь такой аналогией, мы даём фабрике >multThree
число >3
, и, вместо того чтобы выдать число, она возвращает нам фабрику немного поменьше. Эта новая фабрика получает число >5
и тоже выдаёт фабрику. Третья фабрика при получении числа >9
производит, наконец, результат — число >135
. Вспомним, что тип этой функции может быть записан так:
>multThree :: Int -> (Int -> (Int -> Int))
Перед символом >–>
пишется тип параметра функции; после записывается тип значения, которое функция вернёт. Таким образом, наша функция принимает параметр типа