52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 46

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 46

применением или аппликацией.

Выражения, полученные таким образом, напоминают строчную запись дерева, но есть одна тонкость, ко-

торую мы обошли стороной. В случае деревьев мы строили только константы, и конструктор получал столько

аргументов, сколько у него было дочерних узлов (или подтипов). Так мы строили константы. Но в 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 выступают в роли параметров для генерации функций с типом