Выбрать главу

Интерпретатор говорит о том, тип значения 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)))))

Вы можете убедиться насколько гибкими являются числа в Haskelclass="underline"

*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

= Zero

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

fromInteger 0 = Zero

fromInteger n = Succ (fromInteger (n-1))

abs

x

= x

signum Zero = Zero

signum _

= Succ Zero

negate _ = error ”negate is undefined for Nat”

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

Класс Fractional. Деление

Деление определено в классе Fractional:

*Nat>:m Prelude

Prelude> :i Fractional

class Num a => Fractional a where

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

recip :: a -> a

fromRational :: Rational -> a

-- Defined in ‘GHC.Real’

instance Fractional Float -- Defined in ‘GHC.Float’

instance Fractional Double -- Defined in ‘GHC.Float’

Функция recip, это аналог negate для Num. Она делит единицу на данное число. Функция fromRational

строит число данного типа из дробного числа. Если мы пишем 2, то к нему подспудно будет применена

функция fromInteger, а если 2.0, то будет применена функция fromRational.

Стандартные числа

В этом подразделе мы рассмотрим несколько стандартных типов для чисел в Haskell. Все эти числа явля-

ются экземплярами основных численных классов. Тех, которые мы рассмотрели, и многих-многих других.

Целые числа

В Haskell предусмотрено два типа для целых чисел. Это Integer и Int. Чем они отличаются? Значения

типа Integer не ограничены, мы можем проводить вычисления с очень-очень-очень большими числами, если

памяти на нашем компьютере хватит. Числа из типа Int ограничены. Каждое число занимает определённый

размер в памяти компьютера. Диапазон значений для Int составляет от 229 до 229 1. Вычисления с Int

более эффективны.

Действительные числа

Действительные числа бывают дробными (тип Rational), с ординарной точностью Float и с двойной

точностью Double. Числа из типа Float занимают меньше места, но они не такие точные как Double. Если вы

сомневаетесь, чем пользоваться, выбирайте Double, обычно Float используется только там, где необходимо

хранить огромные массивы чисел. В этом случае мы экономим много памяти.

Преобразование численных типов

Во многих языках программирования при сложении или умножении чисел разных типов проводится ав-

томатическое приведение типов. Обычно целые числа становятся действительными, Float превращается в

Double и так далее. Это противоречит строгой типизации, поэтому в Haskell этого нет: