Как известно, аргументами функций могут быть не только переменные базовых типов, но и другие функции. В этом случае появляется понятие функций высшего порядка. Но для рассмотрения функций высшего порядка необходимо ввести понятие функционального типа (или тип, возвращаемый функцией). Пусть некоторая функция f — это функция одной переменной из множества A, принимающая значение из множества B. Тогда по определению:
#(f) : A ® B
Знак #(f) обозначает "тип функции f". Таким образом, типы, в которых есть символ стрелки ®, носят название функциональных типов. Иногда для их обозначения используется более изощренная запись: BA (далее будет использоваться только стрелочная запись, т.к. для некоторых функций их типы чрезвычайно сложно представить при помощи степеней).
Например: #(sin) : Real ® Real
#(Length) : List (A) ® Integer
Для функций многих аргументов определение типа можно выводить при помощи операции декартового произведения (например, #(add(x, y)) : Real ´ Real ® Real). Однако в функциональном программировании такой способ определения типов функций многих переменных не прижился.
В 1924 году М. Шёнфинкель предложил представлять функции многих аргументов как последовательность функций одного аргумента. В этом случае тип функции, которая складывает два действительных числа, выглядит так: Real ® (Real ® Real). Т.е. тип таких функций получается последовательным применением символа стрелки ®. Пояснить этот процесс можно на следующем примере:
Пример 3.1. Тип функции add (x, y).
Предположительно, каждый из аргументов функции add уже означен, пусть x = 5, y = 7. В этом случае из функции add путем удаления первого аргумента получается новая функция — add5, которая прибавляет к своему единственному аргументу число 5. Ее тип получается легко, он по определению таков: Real ® Real. Теперь, возвращаясь назад, можно понять, почему тип функции add равен Real ® (Real ® Real).
Для того чтобы не изощряться с написанием функций типа add5 (как в предыдущем примере), была придумана специальная аппликативная форма записи в виде "оператор – операнд". Предпосылкой для этого послужило новое видение на функции в функциональном программировании. Ведь если традиционно считалось, что выражение f (5) обозначает "применение функции f к значению аргумента, равному 5" (т.е. вычисляется только аргумент), то в функциональном программировании считается, что значение функции также вычисляется. Так, возвращаясь к примеру 3.1, функцию add можно записать как (add (x)) y, а когда аргументы получают конкретные значения (например, (add (5)) 7), сначала вычисляются все функции, пока не появится функция одного аргумента, которая применяется к последнему.
Таким образом, если функция f имеет тип A1 ® (A2 ® ( ... (An ® B) ... )), то чтобы полностью вычислить значение f (a1, a2, ..., an) необходимо последовательно провести вычисление ( ... (f (a1) a2) ... ) an. И результатом вычисления будет объект типа B.
Соответственно выражение, в котором все функции рассматриваются как функции одного аргумента, а единственной операцией является аппликация (применение), называются выражениями в форме "оператор – операнд". Такие функции получили название "каррированные", а сам процесс сведения типа функции к виду, приведенному в предыдущем абзаце — каррированием (по имени Карри Хаскелла).
Если вспомнить l-исчисление, то обнаружится, что в нем уже есть математическая абстракция для аппликативных форм записей. Например:
f (x) = x2 + 5 Û lx.(x2 + 5)
f (x, y) = x + y Û ly.lx.(x + y)
f (x, y, z) = x2 + y2 + z2 Û lz.ly.lx.(x2 + y2 + z2)
И так далее...