52700.fb2
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, это происходит лишь в интерпретаторе, если такая
переменная встретится в программе и компилятор не сможет определить её тип из контекста, произойдёт
ошибка проверки типов, компилятор скажет, что он не смог определить тип. Помочь компилятору можно,
добавив объявление типа с помощью конструкции (v :: T).
Посмотрим ещё раз на определение экземпляра Num для Nat целиком:
instance Num Nat where
(+) a Zero
= a
(+) a (Succ b) = Succ (a + b)
(*) a Zero