Учебник по Haskell | страница 50



части мы составляем новое дерево из тех значений, что были нами получены слева от знака равно. Или

посмотрим на другой пример:

show (Time h m s) = show h ++ ”:” ++ show m ++ ”:” ++ show s

Слева от знака равно мы также выделили из составного дерева (Time h m s) три его дочерних для корня

узла и связали их с переменными h, m и s. А справа от знака равно мы составили из этих переменных новое

выражение.

Итак операцию объявления синонима можно представить в таком виде:

name

декомпозиция

=

композиция

В каждом уравнении у нас три части: новое имя, декомпозиция, поступающих на вход аргументов, и

композиция нового значения. Теперь давайте остановимся поподробнее на каждой из этих операций.

Структура функций | 45

Композиция и частичное применение

Композиция строится по очень простому правилу, если у нас есть значение f типа a -> b и значение x

типа a, мы можем получить новое значение (f x) типа b. Это основное правило построения новых значений,

поэтому давайте запишем его отдельно:

f :: a -> b,

x :: a

--------------------------

(f x) :: b

Сверху от черты, то что у нас есть, а снизу от черты то, что мы можем получить. Это операция называется

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

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

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

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