- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Изучай Haskell во имя добра! - Миран Липовача
Шрифт:
Интервал:
Закладка:
Если экземпляры классов Functor и Monad для типа подчиняются законам функторов и монад, между этими двумя нет никакой разницы (и все монады, которые мы до сих пор встречали, подчиняются обоим). Это примерно как функции pure и return, делающие одно и то же, – только одна имеет ограничение класса Applicative, тогда как другая имеет ограничение Monad.
Давайте опробуем функцию liftM:
ghci> liftM (*3) (Just 8)
Just 24
ghci> fmap (*3) (Just 8)
Just 24
ghci> runWriter $ liftM not $ Writer (True, "горох")
(False,"горох")
ghci> runWriter $ fmap not $ Writer (True, "горох")
(False,"горох")
ghci> runState (liftM (+100) pop) [1,2,3,4]
(101,[2,3,4])
ghci> runState (fmap (+100) pop) [1,2,3,4]
(101,[2,3,4])
Вы уже довольно хорошо знаете, как функция fmap работает со значениями типа Maybe. И функция liftM делает то же самое. При использовании со значениями типа Writer функция отображает первый компонент кортежа, который является результатом. Выполнение функций fmap или liftM с вычислением, имеющим состояние, даёт в результате другое вычисление с состоянием, но его окончательный результат изменяется добавленной функцией. Если бы мы не отобразили функцию pop с помощью (+100) перед тем, как выполнить её, она бы вернула (1, [2,3,4]).
Вот как реализована функция liftM:
liftM :: (Monad m) => (a –> b) –> m a –> m b
liftM f m = m >>= (x –> return (f x))
Или с использованием нотации do:
liftM :: (Monad m) => (a –> b) –> m a –> m b
liftM f m = do
x <– m
return (f x)
Мы передаём монадическое значение m в функцию, а затем применяем функцию к его результату, прежде чем поместить его обратно в контекст по умолчанию. Ввиду монадических законов гарантируется, что функция не изменит контекст; она изменяет лишь результат, который представляет монадическое значение.
Вы видите, что функция liftM реализована совсем не ссылаясь на класс типов Functor. Значит, мы можем реализовать функцию fmap (или liftM – называйте, как пожелаете), используя лишь те блага, которые предоставляют нам монады. Благодаря этому можно заключить, что монады, по крайней мере, настолько же сильны, насколько и функторы.
Класс типов Applicative позволяет нам применять функции между значениями с контекстами, как если бы они были обычными значениями, вот так:
ghci> (+) <$> Just 3 <*> Just 5
Just 8
ghci> (+) <$> Just 3 <*> Nothing
Nothing
Использование этого аппликативного стиля всё упрощает. Операция <$> – это просто функция fmap, а операция <*> – это функция из класса типов Applicative, которая имеет следующий тип:
(<*>) :: (Applicative f) => f (a –> b) –> f a –> f b
Так что это вроде fmap, только сама функция находится в контексте. Нам нужно каким-то образом извлечь её из контекста и с её помощью отобразить значение f a, а затем вновь собрать контекст. Поскольку все функции в языке Haskell по умолчанию каррированы, мы можем использовать сочетание из операций <$> и <*> между аппликативными значениями, чтобы применять функции, принимающие несколько параметров.
Однако, оказывается, как и функция fmap, операция <*> тоже может быть реализована, используя лишь то, что даёт нам класс типов Monad. Функция ap, по существу, – это <*>, только с ограничением Monad, а не Applicative. Вот её определение:
ap :: (Monad m) => m (a –> b) –> m a –> m b
ap mf m = do
f <– mf
x <– m
return (fx)
Функция ap – монадическое значение, результат которого – функция. Поскольку функция, как и значение, находится в контексте, мы берём функцию из контекста и называем её f, затем берём значение и называем его x, и, в конце концов, применяем функцию к значению и представляем это в качестве результата. Вот быстрая демонстрация:
ghci> Just (+3) <*> Just 4
Just 7
ghci> Just (+3) `ap` Just 4
Just 7
ghci> [(+1),(+2),(+3)] <*> [10,11]
[11,12,12,13,13,14]
ghci> [(+1),(+2),(+3)] `ap` [10,11]
[11,12,12,13,13,14]
Теперь нам видно, что монады настолько же сильны, насколько и аппликативные функторы, потому что мы можем использовать методы класса Monad для реализации функций из класса Applicative. На самом деле, когда обнаруживается, что определённый тип является монадой, зачастую сначала записывают экземпляр класса Monad, а затем создают экземпляр класса Applicative, просто говоря, что функция pure – это return, а операция <*> – это ap. Аналогичным образом, если у вас уже есть экземпляр класса Monad для чего-либо, вы можете сделать для него экземпляр класса Functor, просто говоря, что функция fmap – это liftM.
Функция liftA2 весьма удобна для применения функции между двумя аппликативными значениями. Она определена вот так:
liftA2 :: (Applicative f) => (a –> b –> c) –> f a –> f b –> f c
liftA2 f x y = f <$> x <*> y
Функция liftM2 делает то же, но с использованием ограничения Monad. Есть также функции liftM3, liftM4 и liftM5.
Вы увидели, что монады не менее сильны, чем функторы и аппликативные функторы – и, хотя все монады, по сути, являются функторами и аппликативными функторами, у них необязательно имеются экземпляры классов Functor и Applicative. Мы изучили монадические эквиваленты функций, которые используются функторами и аппликативными функторами.
Функция join
Есть кое-какая пища для размышления: если результат монадического значения – ещё одно монадическое значение (одно монадическое значение вложено в другое), можете ли вы «разгладить» их до одного лишь обычного монадического значения? Например, если у нас есть Just (Just 9), можем ли мы превратить это в Just 9? Оказывается, что любое вложенное монадическое значение может быть разглажено, причём на самом деле это свойство уникально для монад. Для этого у нас есть функция join. Её тип таков:
join :: (Monad m) => m (m a) –> m a
Значит, функция join принимает монадическое значение в монадическом значении и отдаёт нам просто монадическое значение; другими словами, она его разглаживает. Вот она с некоторыми значениями типа Maybe:
ghci> join (Just (Just 9))
Just 9
ghci> join (Just Nothing)
Nothing
ghci> join Nothing
Nothing
В первой строке – успешное вычисление как результат успешного вычисления, поэтому они оба просто соединены в одно большое успешное вычисление. Во второй строке значение Nothing представлено как результат значения Just. Всякий раз, когда мы раньше имели дело со значениями Maybe и хотели объединить несколько этих значений – будь то с использованием операций <*> или >>= – все они должны были быть значениями конструктора Just, чтобы результатом стало значение Just. Если на пути возникала хоть одна неудача, то и результатом являлась неудача; нечто аналогичное происходит и здесь. В третьей строке мы пытаемся разгладить то, что возникло вследствие неудачи, поэтому результат – также неудача.
Разглаживание списков осуществляется довольно интуитивно:
ghci> join [[1,2,3],[4,5,6]]
[1,2,3,4,5,6]
Как вы можете видеть, функция join для списков – это просто concat. Чтобы разгладить значение монады Writer, результат которого сам является значением монады Writer, нам нужно объединить моноидное значение с помощью функции mappend:
ghci> runWriter $ join (Writer (Writer (1, "aaa"), "bbb"))
(1,"bbbaaa")
Внешнее моноидное значение "bbb" идёт первым, затем к нему конкатенируется строка "aaa". На интуитивном уровне, когда вы хотите проверить результат значения типа Writer, сначала вам нужно записать его моноидное значение в журнал, и только потом вы можете посмотреть, что находится внутри него.
Разглаживание значений монады Either очень похоже на разглаживание значений монады Maybe:
ghci> join (Right (Right 9)) :: Either String Int
Right 9
ghci> join (Right (Left "ошибка")) :: Either String Int
Left "ошибка"
ghci> join (Left "ошибка") :: Either String Int
Left "ошибка"
Если применить функцию join к вычислению с состоянием, результат которого является вычислением с состоянием, то результатом будет вычисление с состоянием, которое сначала выполняет внешнее вычисление с состоянием, а затем результирующее. Взгляните, как это работает:
ghci> runState (join (state $ s –> (push 10, 1:2:s))) [0,0,0]

