--
действия
data Date
= Date{
day
:: Int,
month
:: Int,
year
:: Int
} deriving( Show, Eq)
В фигурных скобках через запятую мы указываем поля. Поле состоит из имени и типа. Теперь нам до-
ступны две операции:
• Чтение полей
hello :: Passport -> String
hello p =”Hello, ” ++givenName p ++”!”
114 | Глава 7: Функторы и монады: примеры
Для чтения мы просто подставляем в имя поля данное значение. В этой функции мы приветствуем
человека и обращаемся к нему по имени. Для того, чтобы узнать его имя мы подсмотрели в паспорт, в
поле givenName.
• Обновление полей. Для обновления полей мы пользуемся таким синтаксисом:
value { fieldName1 =newValue1, fieldName2 =newValue2, ...}
Мы присваиваем в значении value полю с именем fieldName новое значение newFieldValue. К примеру
продлим срок действия паспорта на десять лет:
prolongate :: Passport -> Passport
prolongate p =p{ dateOfExpiry =newDate }
wherenewDate =oldDate { year =year oldDate +10 }
oldDate =dateOfExpiry p
Вернёмся к типам Sumи Prod:
newtype Sum
a = Sum
{ getSum
::a }
newtype Proda = Prod{ getProd ::a }
Этой записью мы определили два типа-обёртки. У нас есть две функции, которые заворачивают обычное
значение, это Sumи Prod. С помощью записей мы тут же в определении типа определили функции которые
разворачивают значения, это getSum и getProd.
Вспомним определение для типа State:
data States a = State(s ->(a, s))
runState :: States a ->(s ->(a, s))
runState ( Statef) =f
Было бы гораздо лучше определить его так:
newtype States a = State{ runState ::s ->(a, s) }
Накопление чисел
Но вернёмся к нашей задаче. Мы будем накапливать сумму в значении типа Sum. Поскольку нас интере-
сует лишь значение накопителя, наша функция будет возвращать значение единичного типа ().
countBiFuns :: Exp -> Int
countBiFuns =getSum .execWriter .countBiFuns’
countBiFuns’ :: Exp -> Writer( Sum Int) ()
countBiFuns’ x = casex of
Adda b ->tell ( Sum1) *>bi a b
Mula b ->tell ( Sum1) *>bi a b
Nega
->un a
_
->pure ()
wherebi a b =countBiFuns’ a *>countBiFuns’ b
un
=countBiFuns’
tell :: Monoida =>a -> Writera ()
tell a = Writer((), a)
execWriter :: Writermsg a ->msg
execWriter ( Writer(a, msg)) =msg
Первая функция countBiFuns извлекает значение из типов Writerи Sum. А вторая функция countBiFuns’
вычисляет значение.
Мы определили две вспомогательные функции tell, которая записывает сообщение в накопитель и
execWriter, которая возвращает лишь сообщение. Это стандартные для Writerфункции.
Посмотрим как работает эта функция:
*Exp>countBiFuns (n 2)
0
*Exp>countBiFuns (n 2 +n 1 +2 +3)
3
Накопление результата | 115
Накопление логических значений
В модуле Data.Monoidопределены два типа для накопления логических значений. Это типы Allи Any. С
помощью типа Allмы можем проверить выполняется ли некоторое свойство для всех значений. А с помощью
типа Anyмы можем узнать, что существует хотя бы один элемент, для которых это свойство выполнено.
Посмотрим на определение экземпляров класса Monoidдля этих типов:
newtype All = All{ getAll :: Bool}
instance Monoid All where
mempty = All True
Allx ‘mappend‘ Ally = All(x &&y)
В типе Allмы накапливаем значения с помощью логического “и”. Нейтральным элементом является кон-
структор True. Итоговое значение накопителя будет равно Trueтолько в том случае, если все накапливаемые
сообщения были равны True.
В типе Anyвсё наоборот:
instance Monoid Any where
mempty = Any False
Anyx ‘mappend‘ Anyy = Any(x ||y)
Посмотрим как работают эти типы. Составим функцию, которая проверяет отсутствие оператора минус
в выражении:
noNeg :: Exp -> Bool
noNeg =not .getAny .execWriter .anyNeg
anyNeg :: Exp -> Writer Any()
anyNeg x = casex of
Читать дальше