(Succ
(Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))= True
is7
_
= False
С помощью переменных мы даём синонимы поддеревьям. Этими синонимами мы можем пользоваться в
правой части функции. Так в уравнении
addZero (a:[]
)мы извлекаем первый элемент из списка, и одновременно говорим о том, что список может содержать
только один элемент. Отметим, что если мы хотим дать синоним всему дереву а не какой-то части, мы просто
пишем на месте аргумента переменную, как в случае функции xor:
xor a b = ...
С помощью безразличной переменной говорим, что нам не важно, что находится у дерева в этом узле.
Уравнения в определении синонима обходятся сверху вниз, поэтому часто безразличной переменной поль-
зуются в смысле “а во всех остальных случаях”, как в:
instance Eq Nat where
(==
) ZeroZero
= True
(==
) (Succ a) (Succ b) = a == b(==
) __
= False
Переменные и безразличные переменные также могут уходить вглубь дерева сколь угодно далеко (или
ввысь дерева, поскольку первый уровень в строчной записи это корень):
lessThan7 :: Nat -> Bool
lessThan7
(Succ
(Succ (Succ (Succ (Succ (Succ (Succ _)))))))= False
lessThan7
_
= True
Декомпозицию можно применять только к значениям-константам. Проявляется интересная закономер-
ность: если для композиции необходимым элементом было значение со стрелочным типом (функция), то в
случае декомпозиции нам нужно значение с типом без стрелок (константа). Это говорит о том, что все функ-
ции будут полностью применены, то есть константы будут записаны в виде строчной записи дерева. Если мы
ожидаем на входе функцию, то мы можем только дать ей синоним с помощью с помощью переменной или
проигнорировать её безразличной переменной.
Как в
name
(Succ
(Succ Zero))= ...
name
(Zero : Succ Zero : []
)= ...
Но не
name
Succ
= ...
name
(Zero :
)= ...
Отметим, что для композиции это допустимые значения, в первом случае это функция Nat -> Nat
, а вовтором это функция типа [Nat
] -> [Nat].Ещё одна особенность декомпозиции заключается в том, что при декомпозиции мы можем пользоваться
только “настоящими” значениями, то есть конструкторами, объявленными в типах. В случае композиции мы
могли пользоваться как конструкторами, так и синонимами.
Например мы не можем написать в декомпозиции:
name
(add Zero Zero
)= ...
name
(or (xor a b) True
)= ...
В Haskell декомпозицию принято называть
на то, что в аргументе мы выписываем шаблон (или заготовку) для целого набора значений. Наборы значений
могут получиться, если мы пользуемся переменными. Конструкторы дают нам возможность зафиксировать
вид ожидаемого на вход дерева.
Структура функций | 49
3.4 Проверка типов
В этом разделе мы поговорим об ошибках проверки типов. Почти все ошибки, которые происходят в
Haskell, связаны с проверкой типов. Проверка типов происходит согласно правилам применения, которые
встретились нам в разделе о композиции значений. Мы остановимся лишь на случае для префиксной формы
записи, правила для сечений работают аналогично. Давайте вспомним основное правило:
f ::
a -> b,x ::
a--------------------------
(f x) ::
bЧто может привести к ошибке? В этом правиле есть два источника ошибки.
• Тип f не содержит стрелок, или f не является функцией.
• Типы x и аргумента для f не совпадают.
Вот и все ошибки. Универсальное представление всех функций в виде функций одного аргумента, значи-
тельно сокращает число различных видов ошибок. Итак мы можем ошибиться применяя значение к константе
и передав в функцию не то, что она ожидает.
Потренируемся в интерпретаторе, сначала попытаемся создать ошибку первого типа:
*Nat> Zero Zero
<
interactive>:1:1:The
function ‘Zero’ is applied to one argument,but its type
‘Nat’ has noneIn
the expression: Zero ZeroIn
an equation for ‘it’: it = Zero ZeroЕсли перевести на русский интерпретатор говорит:
*Nat> Zero Zero
<
interactive>:1:1:Функция
’Zero’ применяется к одному аргументу,но её тип
’Nat’ не имеет аргументовВ выражении: Zero Zero
В уравнении для
‘it’: it = Zero ZeroКомпилятор увидел применение функции f x, далее он посмотрел, что x = Zero
, из этого на основеправила применения он сделал вывод о том, что f имеет тип Nat ->
t, тогда он заглянул в f и нашёл тамZero :: Nat
, что и привело к несовпадению типов.Составим ещё одно выражение с такой же ошибкой:
*Nat> True Succ
<
interactive>:6:1:The
function ‘True’ is applied to one argument,but its type
‘Bool’ has noneIn
the expression: True SuccIn
an equation for ‘it’: it = True Succ