*Calendar> Time
(Hour 13) (Minute 25) (Second 2)13:
25:02it :: Time
2.5 Автоматический вывод экземпляров классов типов
Для некоторых стандартных классов экземпляры классов типов могут быть выведены автоматически.
Это делается с помощью директивы deriving
. Она пишется сразу после объявления типа. Например так мыможем определить тип и экземпляры для классов Show
и Eq:data T = A | B | C
deriving
(Show, Eq)Отступ за deriving
обязателен, после ключевого слова в скобках указываются классы, которые мы хотимвывести.
2.6 Арифметика
В этом разделе мы обсудим основные арифметические операции. В Haskell много стандартных классов,
которые группируют различные типы операций, есть класс для сравнения на равенство, отдельный класс для
сравнения на больше/меньше, класс для умножения, класс для деления, класс для упорядоченных чисел, и
много других. Зачем такое изобилие классов?
Каждый из классов отвечает независимой группе операций. Есть много объектов, которые можно только
складывать, но нельзя умножать или делить. Есть объекты, для которых сравнение на равенство имеет смысл,
а сравнение на больше/меньше – нет.
Для иллюстрации мы воспользуемся числами Пеано, у них компактное определение, всего два конструк-
тора, которых тем не менее достаточно для описания множества натуральных чисел:
module Nat where
data Nat = Zero | Succ Nat
deriving
(Show, Eq, Ord)Конструктор Zero
указывает на число ноль, а (Succ n) на число следующее за данным числом n. Впоследней строчке мы видим новый класс Ord
, этот класс содержит операции сравнения на больше/меньше:Prelude> :
i Ordclass
(Eq a) => Ord a wherecompare ::
a -> a -> Ordering(<
) :: a -> a -> Bool(>=
) :: a -> a -> Bool(>
) :: a -> a -> Bool(<=
) :: a -> a -> Boolmax ::
a -> a -> amin ::
a -> a -> aАвтоматический вывод экземпляров классов типов | 31
Тип Ordering
кодирует результаты сравнения:Prelude> :
i Orderingdata Ordering = LT | EQ | GT
-- Defined in GHC.Ordering
Он содержит конструкторы, соответствующие таким понятиям как меньше, равно и больше.
Класс Eq. Сравнение на равенство
Вспомним определение класса Eq
:class Eq
a where(==
) :: a -> a -> Bool(/=
) :: a -> a -> Boola ==
b = not (a /= b)a /=
b = not (a == b)Появились две детали, о которых я умолчал в предыдущей главе. Это две последние строчки. В них
мы видим определение ==
через /= и наоборот. Это определения методов по умолчанию. Такие определениядают нам возможность определять не все методы класса, а лишь часть основных, а все остальные мы получим
автоматически из определений по умолчанию.
Казалось бы почему не оставить в классе Eq
один метод а другой метод определить в виде отдельнойфункции:
class Eq
a where(==
) :: a -> a -> Bool(/=
) :: Eq a => a -> a -> Boola /=
b = not (a == b)Так не делают по соображениям эффективности. Есть типы для которых проще вычислить /=
чем ==.Тогда мы определим тот метод, который нам проще вычислять и второй получим автоматически.
Набор основных методов, через которые определены все остальные называют
Мы уже вывели экземпляр для 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 Numclass
(Eq a, Show a) => Num a where(+
) :: a -> a -> a(*
) :: a -> a -> a(-
) :: a -> a -> anegate ::
a -> aabs ::
a -> asignum ::
a -> afromInteger :: Integer ->
a-- Defined in GHC.Num
Методы (+
), (*), (-) в представлении не нуждаются, метод negate является унарным минусом, его можноопределить через (-
) так:32 | Глава 2: Первая программа
negate x =
0 - xМетод abs является модулем числа, а метод signum возвращает знак числа, метод fromInteger позволяет
создавать значения данного типа из стандартных целых чисел Integer
.Этот класс устарел, было бы лучше сделать отельный класс для сложения и вычитания и отдельный
класс для умножения. Также контекст класса, часто становится помехой. Есть объекты, которые нет смысла
печатать но, есть смысл определить на них сложение и умножение. Но пока в целях совместимости с уже
написанным кодом, класс Num
остаётся прежним.