haskell-notes

Функция-накопитель имеет тип: a -> (b, msg)

Выделим результат функции в отдельный тип с именем Writer.

102 | Глава 6: Функторы и монады: теория

a

f

b

Msg

Рис. 6.10: Функция-накопитель

data Writer msg b = Writer (b, msg)

runWriter :: Writer msg b -> (b, msg)

runWriter (Writer a) = a

Тип функции примет вид:

a -> Writer msg b

Значения типа msg мы будем называть сообщениями. Смысл функций a -> Writer msg b заключается

в том, что при вычислении они накапливают в значении msg какую-нибудь информацию. Это могут быть

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

Класс Monoid

Как мы будем накапливать результат? Пока мы умеем лишь возвращать из функции пару значений. Одно

из них нам нужно передать в следующую функцию, а что делать с другим?

На помощь нам придёт класс Monoid, он определён в модуле Data.Monoid:

class Monoid a where

mempty

:: a

mappend :: a -> a -> a

В этом классе определено пустое значение mempty и бинарная функция соединения двух значений в одно.

Этот класс очень похож на класс Category и Kleisli. Там тоже было значение, которое ничего не делает и

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

mempty

‘mappend‘ f

= f

f

‘mappend‘ mempty

= f

f ‘mappend‘ (g ‘mappend‘ h) =

(f ‘mappend‘ g) ‘mappend‘ h

a

g

f

b

b

c

msg

msg

b

a

g

f

c

MsgG

++

MsgF ++ MsgG

MsgF

a

f*>g

c

msg

Рис. 6.11: Композиция функций-накопителей

Упражнения | 103

Первые два свойства говорят о том, что значение mempty и вправду является пустым элементом отно-

сительно операции mappend. А третье свойство говорит о том, что порядок при объединении элементов не

важен.

Посмотрим на определение экземпляра для списков:

instance Monoid [a] where

mempty

= []

mappend = (++)

Итак пустой элемент это пустой список, а объединение это операция конкатенации списков. Проверим в

интерпретаторе:

*Kleisli> :m Data.Monoid

Prelude Data.Monoid> [1 .. 4] ‘mappend‘ [4, 3 .. 1]

[1,2,3,4,4,3,2,1]

Prelude Data.Monoid> ”Hello” ‘mappend‘ ” World” ‘mappend‘ mempty

”Hello World”

Напишите экземпляр класса Kleisli для функций накопителей по (рис. 6.11). При этом будем считать,

что тип msg является экземпляром класса Monoid.

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

Представьте, что у нас нет класса Kleisli, а есть лишь Functor, Applicative и Monad. Напишите экзем-

пляры для этих классов для всех рассмотренных в этой главе специальных функций (в том числе и для Reader

и Writer). Экземпляры Functor и Applicative могут быть определены через Monad. Но для тренировки опре-

делите экземпляры полностью. Сначала Functor, затем Applicative и в последнюю очередь Monad.

Деревья

Напишите экземпляры классов Kleisli и Monad для двух типов, которые описывают деревья. Бинарные

деревья:

data BTree a = BList a | BNode a (BTree a) (BTree a)

Деревья с несколькими узлами:

data Tree a = Node a [Tree a]

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

многозначные функции, которые возвращают несколько значений, организованных в иерархическую струк-

туру.

Стандартные функции

Почитайте документацию к модулям Control.Monad и Control.Applicative. Присмотритесь к функциям,

попробуйте применить их в интерпретаторе.

Эквивалентность классов Kleisli и Monad

Покажите, что классы Kleisli и Monad эквивалентны. Для этого нужно для произвольного типа c с одним

параметром m определить два экземпляра:

instance Kleisli m => Monad

m where

instance Monad

m => Kelisli m where

Нужно определить экземпляр одного класса с помощью методов другого.

Свойства класса Monad

Если класс Monad эквивалентен Kleisli, то в нём должны выполнятся точно такие же свойства. Запишите

свойства класса Kleisli через методы класса Monad

104 | Глава 6: Функторы и монады: теория

Глава 7

Функторы и монады: примеры

В этой главе мы закрепим на примерах то, что мы узнали о монадах и функторах. Напомню, что с по-

мощью монад и функторов мы можем комбинировать специальные функции вида (a -> m b) с другими

специальными функциями.

У нас есть функции тождества и применения:

class Functor f where

fmap :: (a -> b) -> f a -> f b

class Functor f => Applicative f where

pure

:: a -> f a

( )

:: f (a -> b) -> f a -> f b

class Monad m where

return

:: a -> m a

(>>=)

:: m a -> (a -> m b) -> m b

(=<< ) :: (a -> m b) -> m a -> m b

(=<< ) = flip (>>=)

Вспомним основные производные функции для этих классов:

Или в терминах класса Kleisli:

— Композиция

(>=> ) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)

(<=< ) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)

— Константные функции

(*> ) :: Applicative f => f a -> f b -> f b

(<*) :: Applicative f => f a -> f b -> f a

— Применение обычных функций к специальным значениям

( )

:: Functor f => (a -> b) -> f a -> f b

liftA

:: Applicative f => (a -> b)

-> f a -> f b

liftA2 :: Applicative f => (a -> b -> c)

-> f a -> f b -> f c

liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d

— Преобразование элементов списка специальной функцией

mapM

:: Monad m => (a -> m b) -> [a] -> m [b]

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

смотрели в предыдущей главе. Экземпляры для [] и Maybe уже определены в Prelude, а типы State, Reader

и Writer можно найти в библиотеках mtl и transformers. Пока мы не знаем как устанавливать библиотеки

определим эти типы и экземпляры для Monad самостоятельно. Возможно вы уже определили их, выполняя

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

Types:

module Types(

State(.. ), Reader(.. ), Writer(.. ),

runState, runWriter, runReader,

| 105

module Control.Applicative,

module Control.Monad,

module Data.Monoid)

where

import Data.Monoid

import Control.Applicative

import Control.Monad

————————————————-

— Функции с состоянием

a -> State s b

data State s a = State (s -> (a, s))

runState :: State s a -> s -> (a, s)

runState (State f) = f

instance Monad (State s) where

return a

= State $ s -> (a, s)

ma >>= mf = State $ s0 ->

let (b, s1) = runState ma s0

in

runState (mf b) s1

—————————————————

— Функции с окружением

a -> Reader env b

data Reader env a = Reader (env -> a)

runReader :: Reader env a -> env -> a

runReader (Reader f) = f

instance Monad (Reader env) where

return a

= Reader $ const a

ma >>= mf

= Reader $ env ->

let b = runReader ma env

in

runReader (mf b) env

—————————————————

— Функции-накопители

Monoid msg => a -> Writer msg b

data Writer msg a = Writer (a, msg)

deriving (Show)

runWriter :: Writer msg a -> (a, msg)

runWriter (Writer f) = f

instance Monoid msg => Monad (Writer msg) where

return a

= Writer (a, mempty)

ma >>= mf

= Writer (c, msgA ‘mappend‘ msgF)

where (b, msgA) = runWriter ma

(c, msgF) = runWriter $ mf b

Я пропустил определения для экземпляров классов Functor и Applicative, их можно получить из экзем-

Читай продолжение на следующей странице
Добавить комментарии