Если класс 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, их можно получить из экзем-
пляра для класса Monad с помощью стандартных функций liftM, return и ap из модуля Control.Monad.
Нам встретилась новая запись в экспорте модуля. Для удобства мы экспортируем модули
Control.Applicative, Control.Monad и Data.Monoid целиком. Для этого мы написали ключевое слово
module перед экспортируемым модулем. Теперь если мы в каком-нибудь другом модуле импортируем
модуль Types нам станут доступными все функции из этих модулей.
Мы определили экземпляры для Functor и Applicative с помощью производных функций класса Monad.
106 | Глава 7: Функторы и монады: примеры
7.1 Случайные числа
С помощью монады State можно имитировать случайные числа. Мы будем генерировать случайные числа
из интервала от 0 до 1 с помощью алгоритма:
nextRandom :: Double -> Double
nextRandom = snd . properFraction . (105.947 * )
Функция properFraction возвращает пару, которая состоит из целой части и остатка числа. Взяв второй