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



чем ==.

Тогда мы определим тот метод, который нам проще вычислять и второй получим автоматически.

Набор основных методов, через которые определены все остальные называют минимальным полным опре-

делением (minimal complete definition) класса. В случае класса Eq это метод == или метод /=.

Мы уже вывели экземпляр для Eq, поэтому мы можем пользоваться методами == и /= для значений типа

Nat:

*Calendar> :l Nat

[1 of 1] Compiling Nat

( Nat. hs, interpreted )

Ok, modules loaded: Nat.

*Nat> Zero == Succ (Succ Zero)

False

it :: Bool

*Nat> Zero /= Succ (Succ Zero)

True

it :: Bool

Класс Num. Сложение и умножение

Сложение и умножение определены в классе Num. Посмотрим на его определение:

*Nat> :i Num

class (Eq a, Show a) => Num a where

(+) :: a -> a -> a

(*) :: a -> a -> a

(-) :: a -> a -> a

negate :: a -> a

abs :: a -> a

signum :: a -> a

fromInteger :: Integer -> a

-- Defined in GHC.Num

Методы (+), (*), (-) в представлении не нуждаются, метод negate является унарным минусом, его можно

определить через (-) так:

32 | Глава 2: Первая программа

negate x = 0 - x

Метод abs является модулем числа, а метод signum возвращает знак числа, метод fromInteger позволяет

создавать значения данного типа из стандартных целых чисел Integer.

Этот класс устарел, было бы лучше сделать отельный класс для сложения и вычитания и отдельный

класс для умножения. Также контекст класса, часто становится помехой. Есть объекты, которые нет смысла

печатать но, есть смысл определить на них сложение и умножение. Но пока в целях совместимости с уже

написанным кодом, класс Num остаётся прежним.

Определим экземпляр для чисел Пеано, но давайте сначала разберём функции по частям.

Сложение

Начнём со сложения:

instance Num Nat where

(+) a Zero

= a

(+) a (Succ b) = Succ (a + b)

Первое уравнение говорит о том, что, если второй аргумент равен нулю, то мы вернём первый аргумент

в качестве результата. Во втором уравнении мы “перекидываем” конструктор Succ из второго аргумента за

пределы суммы. Схематически вычисление суммы можно представить так:

3+2 1 + (3+1) 1 + (1 + (3+0))

1 + (1 + 3) 1 + (1 + (1 + (1 + (1 + 0)))) 5

Все наши числа имеют вид 0 или 1+ n, мы принимаем на вход два числа в таком виде и хотим в результате

составить число в этом же виде, для этого мы последовательно перекидываем $(1+) в начало выражения из

второго аргумента.

Вычитание

Операция отрицания не имеет смысла, поэтому мы воспользуемся специальной функцией error ::

String -> a, она принимает строку с сообщением об ошибке, при её вычислении программа остановит-