52700.fb2
Выражения, полученные таким образом, напоминают строчную запись дерева, но есть одна тонкость, ко-
торую мы обошли стороной. В случае деревьев мы строили только константы, и конструктор получал столько
аргументов, сколько у него было дочерних узлов (или подтипов). Так мы строили константы. Но в Haskell мы
можем с помощью применения строить функции на лету, передавая меньшее число аргументов, этот процесс
называется частичным применением или каррированием (currying). Поясним на примере, предположим у нас
есть функция двух аргументов:
add :: Nat -> Nat -> Nat
add a b = ...
На самом деле компилятор воспринимает эту запись так:
add :: Nat -> (Nat -> Nat)
add a b = ...
Функция add является функцией одного аргумента, которая в свою очередь возвращает функцию одного
аргумента (Nat -> Nat). Когда мы пишем в где-нибудь в правой части функции:
... =
... (add Zero (Succ Zero)) ...
Компилятор воспринимает эту запись так:
... =
... ((add Zero) (Succ Zero)) ...
Присмотримся к этому выражению, что изменилось? У нас появились новые скобки, вокруг выражения
(add Zero). Давайте посмотрим как происходит применение:
add :: Nat -> (Nat -> Nat),
Zero :: Nat
----------------------------------------------
(add Zero) :: Nat -> Nat
Итак применение функции add к Zero возвращает новую функцию (add Zero), которая зависит от одного
аргумента. Теперь применим к этой функции второе значение:
(add Zero) :: Nat -> Nat,
(Succ Zero) :: Nat
----------------------------------------------
((add Zero) (Succ Zero)) :: Nat
И только теперь мы получили константу. Обратите внимание на то, что получившаяся константа не может
принять ещё один аргумент. Поскольку в правиле для применения функция f должна содержать стрелку, а
у нас есть лишь Nat, это значение может участвовать в других выражениях лишь на месте аргумента.
Тоже самое работает и для функций от большего числа аргументов, если мы пишем
fun :: a1 -> a2 -> a3 -> a4 -> res
... = fun a b c d
На самом деле мы пишем
fun :: a1 -> (a2 -> (a3 -> (a4 -> res)))
... = (((fun a) b) c) d
46 | Глава 3: Типы
Это очень удобно. Так, определив лишь одну функцию fun, мы получили в подарок ещё три функции
(fun a), (fun a b) и (fun a b c). С ростом числа аргументов растёт и число подарков. Если смотреть на
функцию fun, как на функцию одного аргумента, то она представляется таким генератором функций типа
a2 -> a3 -> a4 -> res, который зависит от параметра. Применение функций через пробел значительно
упрощает процесс комбинирования функций.
Поэтому в Haskell аргументы функций, которые играют роль параметров или специфических флагов, то
есть аргументы, которые меняются редко обычно пишутся в начале функции. Например
process :: Param1 -> Param2 -> Arg1 -> Arg2 -> Result
Два первых аргумента функции process выступают в роли параметров для генерации функций с типом