Читаем Учебник по Haskell полностью

тор сможет это установить, но если они окажутся неправильными, может возникнуть такая ситуация, что

компилятор зациклится и будет бесконечно долго искать соответствие одного типа другому. Теперь про-

верим результаты. Для этого мы создадим специальный класс, который будет переводить значения-типы в

обычные целочисленные значения:

class Nat a where

toInt :: a -> Int

instance Nat Zero where

toInt = const 0

instance Nat a => Nat (Succ a) where

toInt x = 1 + toInt (proxy x)

proxy :: f a -> a

proxy = undefined

Расширения | 259

Мы определили для каждого значения-типа экземпляр класса Nat, в котором мы можем переводить типы

в числа. Функция proxy позволяет нам извлечь значение из типа-конструктора Succ, так мы поясняем ком-

пилятору тип значения. При этом мы нигде не пользуемся значениями типов Zero и Succ, ведь у этих типов

нет значений. Поэтому в экземпляре для Zero мы пользуемся постоянной функцией const.

Теперь посмотрим, что у нас получилось:

Prelude> :l Nat

*Nat> let x = undefined :: (Mul (Succ (Succ (Succ Zero))) (Succ (Succ Zero)))

*Nat> toInt x

6

Видно, что с помощью класса Nat мы можем извлечь значение, закодированное в типе. Зачем нам эти

странные типы-значения? Мы можем использовать их в двух случаях. Мы можем кодировать значения в типе

или проводить более тонкую проверку типов.

Помните когда-то мы определяли функции для численного интегрирования. Там точность метода была

жёстко задана в тексте программы:

dt :: Fractional a => a

dt = 1e-3

-- метод Эйлера

int :: Fractional a => a -> [a] -> [a]

int x0 ~(f:fs) = x0 : int (x0 + dt * f) fs

В этом примере мы можем создать специальный тип потоков, у которых шаг дискретизации будет зако-

дирован в типе.

data Stream n a = a :& Stream n a

Параметр n кодирует точность. Теперь мы можем извлекать точность из типа:

dt :: (Nat n, Fractional a) => Stream n a -> a

dt xs = 1 / (fromIntegral $ toInt $ proxy xs)

where proxy :: Stream n a -> n

proxy = undefined

int :: (Nat n, Fractional a) => a -> Stream n a -> Stream n a

int x0 ~(f:& fs) = x0 :& int (x0 + dt fs * f) fs

Теперь посмотрим как мы можем сделать проверку типов более тщательной. Представим, что у нас есть

тип матриц. Известно, что сложение определено только для матриц одинаковой длины, а для умножения

матриц число столбцов одной матрицы должно совпадать с числом колонок другой матрицы. Мы можем

отразить все эти зависимости в целочисленных типах:

data Mat n m a = ...

instance Num a => AdditiveGroup (Mat n m a) where

a ^+^ b

= ...

zeroV

= ...

negateV a

= ...

mul :: Num a => Mat n m a -> Mat m k a -> Mat n k a

При таких определениях мы не сможем сложить матрицы разных размеров. Причём ошибка будет вычис-

лена до выполнения программы. Это освобождает от проверки границ внутри алгоритма умножения матриц.

Если алгоритм запустился, то мы знаем, что размеры аргументов соответствуют.

Скоро в ghc появится поддержка чисел на уровне типов. Это будет специальное расширение

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

также будут определены операции-семейства типов на численных типах с привычными именами +, *.

Классы с несколькими типами

Рассмотрим несколько полезных расширений, относящихся к определению классов и экземпляров клас-

сов. Расширение MultiParamTypeClasses позволяет объявлять классы с несколькими аргументами. Например

взгляните на такой класс:

class Iso a b where

to

:: a -> b

from

:: b -> a

Так мы можем определить изоморфизм между типами a и b

260 | Глава 17: Дополнительные возможности

Экземпляры классов для синонимов

Расширение TypeSynonymInstances позволяет определять экземпляры для синонимов типов. Мы уже

пользовались этим расширением, когда определяли рекурсивные типы через тип Fix, там нам нужно бы-

ло определить экземпляр Num для синонима Nat:

type Nat = Fix N

instance Num Nat where

В рамках стандарта все суперклассы должны быть простыми. Все они имеют вид T a. Если мы хотим хотим

использовать суперклассы с составными типами, нам придётся подключить расширение FlexibleContexts.

Этим расширением мы пользовались, когда определяли экземпляр Show для Fix:

instance Show (f (Fix f)) => Show (Fix f) where

show x = ”(” ++ show (unFix x) ++ ”)”

Функциональные зависимости

Класс можно представить как множество типов, для которых определены данные операции. С появлением

расширения MultiParamTypeClasses мы можем определять операции класса для нескольких типов. Так наше

множество классов превращается в отношение. Наш класс связывает несколько типов между собой. Если из

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных