- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Изучай Haskell во имя добра! - Миран Липовача
Шрифт:
Интервал:
Закладка:
ПРИМЕЧАНИЕ. Функция return ничем не похожа на оператор return из других языков программирования, таких как C++ или Java. Она не завершает выполнение функции. Она просто принимает обычное значение и помещает его в контекст.
Следующей функцией является >>=, или связывание. Она похожа на применение функции, но вместо того, чтобы получать обычное значение и передавать его обычной функции, она принимает монадическое значение (то есть значение с контекстом) и передаёт его функции, которая принимает обычное значение, но возвращает монадическое.
Затем у нас есть операция >>. Мы пока не будем обращать на неё большого внимания, потому что она идёт в реализации по умолчанию, и её редко реализуют при создании экземпляров класса Monad. Мы подробно рассмотрим её в разделе «Банан на канате».
Последним методом в классе типов Monad является функция fail. Мы никогда не используем её в нашем коде явно. Вместо этого её использует язык Haskell, чтобы сделать возможным неуспешное окончание вычислений в специальной синтаксической конструкции для монад, с которой вы встретитесь позже. Нам не нужно сейчас сильно беспокоиться по поводу этой функции.
Теперь, когда вы знаете, как выглядит класс типов Monad, давайте посмотрим, каким образом для типа Maybe реализован экземпляр этого класса!
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
Функция return аналогична функции pure, так что для работы с ней не нужно большого ума. Мы делаем то же, что мы делали в классе типов Applicative, и оборачиваем в конструктор Just. Операция >>= аналогична нашей функции applyMaybe. Когда мы передаём значение типа Maybe a нашей функции, то запоминаем контекст и возвращаем значение Nothing, если значением слева является Nothing. Ещё раз: если значение отсутствует, нет способа применить к нему функцию. Если это значение Just, мы берём то, что находится внутри, и применяем к этому функцию f.
Мы можем поиграть с типом Maybe как с монадой:
ghci> return "ЧТО" :: Maybe String
Just "ЧТО"
ghci> Just 9 >>= x –> return (x*10)
Just 90
ghci> Nothing >>= x –> return (x*10)
Nothing
В первой строке нет ничего нового или захватывающего, поскольку мы уже использовали функцию pure с типом Maybe, и мы знаем, что функция return – это просто функция pure под другим именем.
Следующая пара строк демонстрирует операцию >>= уже поинтереснее. Обратите внимание: когда мы передавали значение Just 9 анонимной функции x –> return (x*10), то параметр x принимал значение 9 внутри функции. Выглядит это так, будто мы могли извлечь значение из обёртки Maybe без сопоставления с образцом. И мы всё ещё не потеряли контекст нашего значения Maybe, потому что когда оно равно Nothing, результатом использования операции >>= тоже будет Nothing.
Прогулка по канату
Теперь, когда вы знаете, как передавать значение типа Maybe a функции типа a –> Maybe b, учитывая контекст возможной неудачи в вычислениях, давайте посмотрим, как можно многократно использовать операцию >>= для обработки
вычислений нескольких значений Maybe a.
Пьер решил сделать рабочий перерыв на рыбной ферме и попробовать заняться канатоходством. На удивление, ему это неплохо удаётся, но есть одна проблема: на балансировочный шест приземляются птицы! Они прилетают, немного отдыхают, болтают со своими пернатыми друзьями, а затем срываются в поисках хлебных крошек. Это не сильно беспокоило бы Пьера, будь количество птиц c левой стороны шеста всегда равным количеству птиц с правой стороны. Но порой всем птицам почему-то больше нравится одна сторона. В результате канатоходец теряет равновесие и падает (не волнуйтесь, он использует сетку безопасности!).
Давайте предположим, что Пьер удержит равновесие, если количество птиц на левой стороне шеста и на правой стороне шеста разнится в пределах трёх. Покуда, скажем, на правой стороне одна птица, а на левой – четыре, всё в порядке. Но стоит пятой птице опуститься на левую сторону, канатоходец теряет равновесие и кубарем летит вниз.
Мы сымитируем посадку и улёт птиц с шеста и посмотрим, останется ли Пьер на канате после некоторого количества прилётов и улётов птиц. Например, нам нужно увидеть, что произойдёт с Пьером, если первая птица прилетит на левую сторону, затем четыре птицы займут правую, а потом птица, которая была на левой стороне, решит улететь.
Код, код, код
Мы можем представить шест в виде простой пары целых чисел. Первый компонент будет обозначать количество птиц на левой стороне, а второй – количество птиц на правой:
type Birds = Int
type Pole = (Birds, Birds)
Сначала мы создали синоним типа для Int, названный Birds, потому что мы используем целые числа для представления количества имеющихся птиц. Затем создали синоним типа (Birds, Birds) и назвали его Pole (учтите: это означает «шест» – ничего общего ни с поляками, ни с человеком по имени Поль).
А теперь как насчёт того, чтобы добавить функции, которые принимают количество птиц и производят их приземление на одной стороне шеста или на другой?
landLeft :: Birds –> Pole –> Pole
landLeft n (left, right) = (left + n, right)
landRight :: Birds –> Pole –> Pole
landRight n (left, right) = (left, right + n)
Давайте проверим их:
ghci> landLeft 2 (0, 0)
(2,0)
ghci> landRight 1 (1, 2)
(1,3)
ghci> landRight (-1) (1,2)
(1,1)
Чтобы заставить птиц улететь, мы просто произвели приземление отрицательного количества птиц на одной стороне. Поскольку приземление птицы на Pole возвращает Pole, мы можем сцепить применения функций landLeft и landRight:
ghci> landLeft 2 (landRight 1 (landLeft 1 (0, 0)))
(3,1)
Когда мы применяем функцию landLeft 1 к значению (0, 0), у нас получается результат (1, 0). Затем мы усаживаем птицу на правой стороне, что даёт в результате (1, 1). Наконец, две птицы приземляются на левой стороне, что даёт в результате (3, 1). Мы применяем функцию к чему-либо, сначала записывая функцию, а затем её параметр, но здесь было бы лучше, если бы первым шел шест, а потом функция посадки. Предположим, мы создали вот такую функцию:
x -: f = f x
Можно применять функции, сначала записывая параметр, а затем функцию:
ghci> 100 -: (*3)
300
ghci> True -: not
False
ghci> (0, 0) -: landLeft 2
(2,0)
Используя эту форму, мы можем многократно производить приземление птиц на шест в более «читабельном» виде:
ghci> (0, 0) -: landLeft 1 -: landRight 1 -: landLeft 2
(3,1)
Круто!.. Эта версия эквивалентна предыдущей, где мы многократно усаживали птиц на шест, но выглядит она яснее. Здесь очевиднее, что мы начинаем с (0, 0), а затем усаживаем одну птицу слева, потом одну – справа, и в довершение две – слева.
Я улечу
Пока всё идёт нормально, но что произойдёт, если десять птиц приземлятся на одной стороне?
ghci> landLeft 10 (0, 3)
(10,3)
Десять птиц с левой стороны и лишь три с правой?! Этого достаточно, чтобы отправить в полёт самого Пьера!.. Довольно очевидная вещь. Но что если бы у нас была примерно такая последовательность посадок:
ghci> (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
(0,2)
Может показаться, что всё хорошо, но если вы проследите за шагами, то увидите, что на правой стороне одновременно находятся четыре птицы – а на левой ни одной! Чтобы исправить это, мы должны ещё раз взглянуть на наши функции landLeft и landRight.
Необходимо дать функциям landLeft и landRight возможность завершаться неуспешно. Нам нужно, чтобы они возвращали новый шест, если равновесие поддерживается, но завершались неуспешно, если птицы приземляются неравномерно. И какой способ лучше подойдёт для добавления к значению контекста неудачи, чем использование типа Maybe? Давайте переработаем эти функции:
landLeft :: Birds –> Pole –> Maybe Pole
landLeft n (left,right)
| abs ((left + n) - right) < 4 = Just (left + n, right)
| otherwise = Nothing
landRight :: Birds –> Pole –> Maybe Pole
landRight n (left,right)
| abs (left - (right + n)) < 4 = Just (left, right + n)
| otherwise = Nothing
Вместо того чтобы вернуть значение типа Pole, эти функции теперь возвращают значения типа Maybe Pole. Они по-прежнему принимают количество птиц и прежний шест, как и ранее, но затем проверяют, выведет ли Пьера из равновесия приземление такого количества птиц. Мы используем охранные выражения, чтобы проверить, меньше ли разница в количестве птиц на новом шесте, чем 4. Если меньше, оборачиваем новый шест в конструктор Just и возвращаем это. Если не меньше, возвращаем значение Nothing, сигнализируя о неудаче.

