Учебник по 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, она принимает строку с сообщением об ошибке, при её вычислении программа остановит-