Подумав еще немного об этом, я думаю, что полное объяснение должно быть примерно таким:
Функция Haskell может принимать только один аргумент и возвращать один параметр.
Haskell позволяет нам делать вид, что передано несколько аргументов, но эта форма рассматривается как серия вложенных лямбда-функций.
f x y = x + y
рассматривается как
(1) f = \x -> \y -> x + y
Это лечение верно и для лямбда-функций
\ x y -> x + y
рассматривается как
\ x -> \ y -> x + y
Это позволяет нам рассматривать объявление типа как левоассоциативное, то есть:
f :: Int -> Int -> Int
на самом деле
f :: (Int -> (Int -> Int))
который точно соответствует (1) выше: f не имеет аргументов, но возвращает функцию, которая принимает Int. Эта функция, в свою очередь, возвращает функцию, которая принимает другое Int и возвращает Int.
Это означает, что если мы хотим вернуть функцию из функции, нам не нужно делать ничего особенного, так как это режим по умолчанию в Haskell.
Это также означает, что с учетом объявления типа
f :: Int -> Int -> Int
Мы можем написать реализацию f («уравнение») с 0, 1 или 2 параметрами. Если указан один или два параметра, компилятор сгенерирует необходимые лямбды для соответствия форме
f :: (Int -> (Int -> Int))
f = \x -> \y -> x + y
f x = \y -> x + y -- \x -> is generated
f x y = x + y -- \x -> \y is generated
Но в каждом из этих случаев приложение функции, принимающее два параметра, будет успешно скомпилировано, поскольку оно всегда будет переведено в первую форму, например
f 4 5 --> (\x -> (\y -> x + y) 5 ) 4
Где приложение внутренней функции вернет каррированную форму (x + 5)
Это позволяет использовать частичную функцию, где мы можем дать f только один параметр и вернуть частичную функцию.
Также меняется приоритет в типе функции:
f'' :: (Int -> Int) -> Int
меняет значение - мы принимаем функцию, получающую Int и возвращающую в качестве единственного параметра, и возвращаем Int.
Предполагая, что мы где-то определили эту функцию, затем вызываем эту функцию с целочисленными параметрами, например,
f'' 4 5
не скомпилируется.
Edit:
Кроме того, даже если последние аргументы указаны в скобках или являются объявлением типа, это имеет место.
Например, в последнем случае последняя пара скобок является необязательной, поскольку, если бы их не было, компилятор в любом случае поместил бы их, чтобы получить форму "lambda'd".
t4 :: (Int -> Int) -> (Int -> Int)
t4 f i = f i + i
Может применяться таким образом:
t4 (\x -> x*10) 5
Также дано:
type MyIntInt = Int -> Int
Тогда:
t5 :: MyIntInt -> MyIntInt
Эквивалентно t4, но сбивает с толку, поскольку значение MyIntInt в обоих местах различно.
Первый тип первого параметра.
Второй «расширен» до Int -> Int (возможно, из-за правой ассоциативности оператора), что означает, что t5 может принимать от 0 до 2 параметров в уравнении функции и приложении функции (в то время как фактически всегда принимает 0 и возврат встроенных лямбд, как я подробно описал выше).
например. мы можем написать t5 так же, как t4:
t5 f i = f i + i