52700.fb2
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 мы можем определять операции класса для нескольких типов. Так наше
множество классов превращается в отношение. Наш класс связывает несколько типов между собой. Если из
одной компоненты отношения однозначно следует другая, такое отношение принято называть функцией.
Например обычную функцию одного аргумента можно представить как множество пар (x, f x). Для того
чтобы множество таких пар было функцией необходимо, чтобы выполнялось свойство:
forall x, y.
x == y => f x == f y
Для одинаковых входов мы получаем одинаковые выходы. С функциональными зависимостями мы мо-
жем ввести такое ограничение на классы с несколькими аргументами. Рассмотрим практический пример.
Библиотека Boolean определяет обобщённые логические значения,
class Boolean b where
true, false :: b
notB
:: b -> b
(&&*), (||*) :: b -> b -> b
Логические значения определены в терминах простейших операций, теперь мы можем обобщить связку