Учебник по Haskell | страница 39
ся с ошибкой и сообщение будет выведено на экран.
negate _ = error ”negate is undefined for Nat”
Умножение
Теперь посмотрим на умножение:
(*) a Zero
= Zero
(*) a (Succ b) = a + (a * b)
В первом уравнении мы вернём ноль, если второй аргумент окажется нулём, а во втором мы за каждый
конструктор Succ во втором аргументе прибавляем к результату первый аргумент. В итоге, после вычисле-
ния a * b мы получим аргумент a сложенный b раз. Это и есть умножение. При этом мы воспользовались
операцией сложения, которую только что определили. Посмотрим на схему вычисления:
3*2 → 3 + (3*1) → 3 + (3 + (3*0)) → 3 + (3+0) → 3+3 →
1 + (3+2) → 1 + (1 + (3+1)) → 1 + (1 + (1 + (3+0))) →
1 + (1 + 1 + 3) → 1 + (1 + (1 + (1 + (1 + (1 + 0))))) → 6
Операции abs и signum
Поскольку числа у нас положительные, то методы abs и signum почти ничего не делают:
abs
x
= x
signum Zero = Zero
signum _
= Succ Zero
Арифметика | 33
Перегрузка чисел
Остался последний метод fromInteger. Он конструирует значение нашего типа из стандартного:
fromInteger 0 = Zero
fromInteger n = Succ (fromInteger (n-1))
Зачем он нужен? Попробуйте узнать тип числа 1 в интерпретаторе:
*Nat> :t 1
1 :: (Num t) => t
Интерпретатор говорит о том, тип значения 1 является некоторым типом из класса Num. В Haskell обозна-
чения для чисел перегружены. Когда мы пишем 1 на самом деле мы пишем (fromInteger (1::Integer)).
Поэтому теперь мы можем не писать цепочку Succ-ов, а воспользоваться методом fromInteger, для этого
сохраним определение экземпляра для Num и загрузим обновлённый модуль в интерпретатор:
[1 of 1] Compiling Nat
( Nat. hs, interpreted )
Ok, modules loaded: Nat.
*Nat> 7 :: Nat
Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))
*Nat> (2 + 2) :: Nat
Succ (Succ (Succ (Succ Zero)))
*Nat> 2 * 3 :: Nat
Succ (Succ (Succ (Succ (Succ (Succ Zero)))))
Вы можете убедиться насколько гибкими являются числа в Haskell:
*Nat> (1 + 1) :: Nat
Succ (Succ Zero)
*Nat> (1 + 1) :: Double
2.0
*Nat> 1 + 1
2
Мы выписали три одинаковых выражения и получили три разных результата, меняя объявление типов. В
последнем выражении тип был приведён к Integer. Это поведение интерпретатора по умолчанию. Если мы
напишем:
*Nat> let q = 1 + 1
*Nat> :t q
q :: Integer
Мы видим, что значение q было переведено в Integer, это происходит лишь в интерпретаторе, если такая
переменная встретится в программе и компилятор не сможет определить её тип из контекста, произойдёт
ошибка проверки типов, компилятор скажет, что он не смог определить тип. Помочь компилятору можно,