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

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

Например обычную функцию одного аргумента можно представить как множество пар (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

Логические значения определены в терминах простейших операций, теперь мы можем обобщить связку

if-then-else и классы Eq и Ord:

class Boolean bool => IfB bool a | a -> bool where

ifB :: bool -> a -> a -> a

class Boolean bool => EqB bool a | a -> bool where

(==*), (/=*) :: a -> a -> bool

class Boolean bool => OrdB bool a | a -> bool where

(<*), (>=*), (>*), (<=*) :: a -> a -> bool

Каждый из классов определён на двух типах. Один из них играет роль обычных логических значений, а

второй тип~– это такой же параметр как и в обычных классах из модуля Prelude. В этих определениях нам

встретилась новая конструкция: за переменными класса через разделитель “или” следует что-то похожее на

тип функции. В этом типе мы говорим, что из типа a следует тип bool, или тип a однозначно определяет тип

bool. Эта информация помогает компилятору выводить типы. Если он встретит в тексте выражение v = a <*

b и тип одного из аргументов a или b известен, то тип v будет определён по зависимости.

Зачем нам может понадобиться такая система классов? Например, с ней мы можем определить экземпляр

Boolean для предикатов или функций вида a -> Bool и затем определить три остальных класса для функций

вида a -> b. Мы сравниваем не отдельные логические значения, а функции которые возвращают логические

значения. Так в выражении ifB c t e функция c играет роль “маски”, если на данном значении функция c

вернт истину, то мы воспользуемся значением функции t, иначе возьмём результат из функции e. Например

так мы можем определить функцию модуля:

*Boolean> let absolute = ifB (> 0) id negate

*Boolean> map absolute [-10 .. 10]

[10,9,8,7,6,5,4,3,2,1,0,1,2,3,4,5,6,7,8,9,10]

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

Мы можем указать несколько зависимостей (через запятую) или зависимость от нескольких типов (через

пробел, слева от стрелки):

class C a b c | a -> b, b c -> a where

...

Отметим, что многие функциональные зависимости можно выразить через семейства типов. Пример из

библиотеки Boolean можно было бы записать так:

class Boolean a where

true, false

:: a

(&&*), (||*)

:: a -> a -> a

class Boolean (B a) => IfB a where

type B a :: *

ifB :: (B a) -> a -> a -> a

class IfB a => EqB a where

(==*), (/=*) :: a -> a -> B a

class IfB a => OrdB a where

(<*), (>*), (>=*), (<=*) :: a -> a -> B a

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

Hackage определены в разных вариантах. Семейства типов используются более охотно.

Ограничение мономорфизма

В Haskell мы можем не писать типы функций. Они будут выведены компилятором автоматически. Но

написание типов функций считается признаком хорошего стиля. Поскольку по типам можно догадаться чем

функция занимается. Но есть в правиле вывода типов одно исключение. Если мы напишем:

f = show

То компилятор сообщит нам об ошибке. Это выражение приводит к ошибке, которая вызвана ограничени-

ем мономорфизма. Мы говорили о нём в главе о типах. Часто в сильно обобщённых библиотеках, с больши-

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

Parsec. С этим ограничением приходится писать огромные объявления типов для крохотных выражений.

Что-то вроде:

fun :: (Stream s m t, Show t) => ParsecT s u m a -> ParsecT s u m [a]

fun = g . h (q x) y

И так для любого выражения. В этом случае лучше просто выключить ограничение, добавив в начало

файла:

{-# Language NoMonomorphismRestriction #-}

Полиморфизм высших порядков

Когда мы говорили об ST нам встретилась функция с необычным типом:

runST :: (forall s. ST s a) -> a

Слово forall обозначает для любых. Любой полиморфный тип в Haskell подразумевает, что он определён

для любых типов. Например, когда мы пишем:

reverse :: [a] -> [a]

map

:: (a -> b) -> [a] -> [b]

На самом деле мы пишем:

reverse :: forall a. [a] -> [a]

map

:: forall a b. (a -> b) -> [a] -> [b]

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

По названию слова forall может показаться, что оно несёт в себе много свободы. Оно говорит о том, что

функция определена для любых типов. Но если присмотреться, то эта свобода оказывается жёстким огра-

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

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

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

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

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

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

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

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

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