Вам кажется, что функциональное программирование — это нечто сложное, доступное только гуру программирования? Эта книга развенчает миф об элитарности и позволит любому программисту с легкостью разобраться в хитросплетениях кода.
От знакомых и простых идей ООП вы перейдете к ФП, рассматривая его на простых примерах, захватывающих упражнениях и большом количестве иллюстраций.
Вы начнете с решения простых и маленьких задач, иллюстрирующих базовые понятия, такие как чистые функции и неизменяемые данные, научитесь писать код, лишенный типичных ошибок, обусловленных наличием сложного распределенного состояния, разберетесь с подходами к реализации вводавывода, параллельного выполнения и потоковой передачи данных. К концу книги вы будете создавать ясный функциональный код, который легко читается, тестируется и сопровождается.
Author(s): Михал Плахта
Series: Библиотека программиста
Edition: 1
Publisher: Питер
Year: 2024
Language: Russian
Commentary: Publisher's PDF
Pages: 512
City: СПб.
Tags: Multithreading; Concurrency; Functional Programming; Stream Processing; Scala; Error Handling; Testing; Pure Functions
Предисловие
Благодарности
Об этой книге
Кому адресована книга
Структура издания
О примерах программного кода
Об авторе
От издательства
Часть IФункциональный инструментарий
1. Изучение функционального программирования
Возможно, вы купили эту книгу потому, что...
Что нужно знать перед тем, как начать
Как выглядят функции
Встречайте: функция
Когда код лжет...
Императивный и декларативный стили
Перерыв на кофе: императивный и декларативный стили
Объяснение для перерыва на кофе: императивный и декларативный стили
Насколько полезно изучать функциональное программирование
Прыжок в Scala
Практика функций в Scala
Подготовка инструментов
Знакомство с REPL
Пишем свои первые функции!
Как использовать эту книгу
Резюме
2. Чистые функции
Зачем нужны чистые функции
Императивное решение
Ошибка в коде
Передача копий данных
Ошибка в коде... снова
Повторные вычисления вместо сохранения
Сосредоточение внимания на логике путем передачи состояния
Куда пропало состояние
Разница между чистыми и нечистыми функциями
Перерыв на кофе: преобразование в чистую функцию
Объяснение для перерыва на кофе: преобразование в чистую функцию
Мы доверяем чистым функциям
Чистые функции в языках программирования
Трудно оставаться чистым...
Чистые функции и чистый код
Перерыв на кофе: чистая или нечистая
Объяснение для перерыва на кофе: чистая или нечистая
Использование Scala для написания чистых функций
Практика чистых функций в Scala
Тестирование чистых функций
Перерыв на кофе: тестирование чистых функций
Объяснение для перерыва на кофе: тестирование чистых функций
Резюме
3. Неизменяемые значения
Топливо для двигателя
Еще один пример неизменяемости
Можно ли доверять этой функции
Изменяемость опасна
Функции, которые лгут... снова
Борьба с изменяемостью за счет использования копий
Перерыв на кофе: обжигаемся на изменяемости
Объяснение для перерыва на кофе: обжигаемся на изменяемости
Знакомьтесь: общее изменяемое состояние
Влияние состояния на возможность программирования
Работа с движущимися частями
Работа с движущимися частями в ФП
Неизменяемые значения в Scala
Вырабатываем интуитивное понимание неизменности
Перерыв на кофе: неизменяемый тип String
Объяснение для перерыва на кофе: неизменяемый тип String
Постойте... Разве это не плохо?
Чисто функциональный подход к общему изменяемому состоянию
Практика работы с неизменяемыми списками
Резюме
4. Функции как значения
Реализация требований в виде функций
Нечистые функции и изменяемые значения наносят ответный удар
Использование Java Streams для сортировки списка
Сигнатуры функций должны рассказывать всю правду
Изменение требований
Мы можем передавать код в аргументах!
Использование значений Function в Java
Использование синтаксиса Function для устранения повторяющегося кода
Передача пользовательских функций в виде аргументов
Перерыв на кофе: функции как параметры
Объяснение для перерыва на кофе: функции как параметры
Проблемы с чтением функционального кода на Java
Передача функций в Scala
Глубокое погружение в sortBy
Сигнатуры с параметрами-функциями в Scala
Передача функций в виде аргументов в Scala
Практика передачи функций
Использование декларативного программирования
Передача функций пользовательским функциям
Маленькие функции и их обязанности
Передача встроенных функций
Перерыв на кофе: передача функций в Scala
Объяснение для перерыва на кофе: передача функций в Scala
Чего еще можно добиться, просто передавая функции
Применение функции к каждому элементу списка
Применение функции к каждому элементу списка с помощью map
Знакомство с map
Практика map
Изучите однажды, используйте постоянно
Возврат части списка, соответствующей условию
Возврат части списка с помощью filter
Знакомство с filter
Практика filter
Насколько далеко мы зашли в нашем путешествии...
Не повторяйся?
Легко ли использовать мой API
Добавления нового параметра недостаточно
Функции могут возвращать функции
Использование функций, возвращающих функции
Функции — это значения
Перерыв на кофе: возврат функций
Объяснение для перерыва на кофе: возврат функций
Проектирование функциональных API
Итеративный дизайн функциональных API
Возврат функций из возвращаемых функций
Как вернуть функцию из возвращаемой функции
Использование гибкого API, построенного с использованием возвращаемых функций
Использование нескольких списков параметров в функциях
У нас есть карринг!
Практика каррирования
Программирование с передачей функций в виде значений
Свертка множества значений в одно
Свертка множества значений в одно с помощью foldLeft
Знакомство с foldLeft
Каждый должен знать и уметь пользоваться foldLeft
Практика foldLeft
Моделирование неизменяемых данных
Использование типов-произведений с функциями высшего порядка
Более лаконичный синтаксис встроенных функций
Резюме
Часть IIФункциональные программы
5. Последовательные программы
Написание конвейерных алгоритмов
Составление больших программ из мелких деталей
Императивный подход
flatten и flatMap
Практические примеры использования flatMap
flatMap и изменение размера списка
Перерыв на кофе: работа со списками списков
Объяснение для перерыва на кофе: работа со списками списков
Объединение в цепочку вызовов flatMap и map
Вложенные вызовы flatMap
Значения, зависящие от других значений
Практика использования вложенных вызовов flatMap
Улучшенный синтаксис вложенных вызовов flatMap
for-выражения во спасение!
Перерыв на кофе: flatMap и for-выражение
Объяснение перерыва на кофе: flatMap и for-выражение
Знакомство с for-выражениями
Это не тот for, который вы знаете!
Внутреннее устройство for-выражения
Более сложные for-выражения
Проверка всех комбинаций с помощью for-выражения
Приемы фильтрации
Перерыв на кофе: методы фильтрации
Объяснение для перерыва на кофе: методы фильтрации
В поисках большей абстракции
Сравнение map, foldLeft и flatMap
Использование for-выражений с множествами Set
Использование for-выражений с данными нескольких типов
Практика for-выражений
Определение for-выражения... снова
Использование for-выражений с типами, не являющимися коллекциями
Избегайте значений null: тип Option
Парсинг в виде конвейера
Перерыв на кофе: парсинг с Option
Объяснение перерыва на кофе: парсинг с Option
Резюме
6. Обработка ошибок
Изящная обработка множества различных ошибок
Возможно ли вообще справиться со всеми ними
Сортировка списка телесериалов по продолжительности их выхода
Реализация требования сортировки
Обработка данных, поступающих из внешнего мира
Функциональный дизайн: конструирование из небольших блоков
Парсинг строк в неизменяемые объекты
Парсинг списка — это парсинг одного элемента
Парсинг String в TvShow
А как насчет возможных ошибок?
Является ли возврат null хорошей идеей?
Как наиболее изящно обрабатывать потенциальные ошибки
Реализация функции, возвращающей Option
Option вынуждает обрабатывать возможные ошибки
Конструирование из небольших блоков
Функциональный дизайн составляется из маленьких блоков
Написание небольшой безопасной функции, возвращающей Option
Функции, значения и выражения
Практика безопасных функций, возвращающих Option
Как распространяются ошибки
Значения представляют ошибки
Option, for-выражения и контролируемые исключения...
Не лучше ли использовать контролируемые исключения?
Условное восстановление
Условное восстановление в императивном стиле
Условное восстановление в функциональном стиле
Контролируемые исключения не комбинируются друг с другом, в отличие от значений Option!
Как работает orElse
Практика функциональной обработки ошибок
Функции комбинируются даже при наличии ошибок
Компилятор напоминает, что ошибки должны быть обработаны
Ошибки компиляции нам на пользу!
Преобразование списка значений Option в простой список
Пусть компилятор будет нашим проводником...
... но не будем слишком доверять компилятору!
Перерыв на кофе: стратегии обработки ошибок
Объяснение для перерыва на кофе: стратегии обработки ошибок
Две разные стратегии обработки ошибок
Стратегия обработки ошибок «все или ничего»
Свертка списка значений Option в значение Option со списком
Теперь мы знаем, как обработать множество ошибок одновременно!
Как узнать, в чем причина неудачи
Мы должны передать информацию об ошибке в возвращаемом значении
Передача сведений об ошибке с использованием Either
Переход на использование Either
Возврат Either вместо Option
Практика безопасных функций, возвращающих Either
Навыки работы с Option пригодились и для работы с Either
Перерыв на кофе: обработка ошибок с использованием Either
Объяснение для перерыва на кофе: обработка ошибок с использованием Either
Работа с Option/Either
Резюме
7. Требования как типы
Моделирование данных для минимизации ошибок программистов
Хорошо смоделированные данные не лгут
Проектирование с использованием уже известного нам (простых типов)
Использование данных, смоделированных как простые типы
Перерыв на кофе: недостатки простых типов
Объяснение для перерыва на кофе: недостатки простых типов
Проблемы использования простых типов в моделях
Использование простых типов усложняет нашу работу!
Новые типы защищают от передачи параметров не на своих местах
Использование новых типов в моделях данных
Практика использования новых типов
Гарантии возможности только допустимых комбинаций данных
Моделирование возможности отсутствия данных
Изменения в модели вызывают изменения в логике
Использование данных, смоделированных как значения Option
Функции высшего порядка решают!
Вероятно, для решения этой проблемы существует функция высшего порядка!
Перерыв на кофе: forall/exists/contains
Объяснение для перерыва на кофе: forall/exists/contains
Объединение понятий внутри одного типа-произведения
Моделирование конечных диапазонов значений
Использование типа-суммы
Улучшение модели с помощью типов-сумм
Использование комбинации «тип-сумма + тип-произведение»
Типы-произведения + типы-суммы = алгебраические типы данных (ADT)
Использование моделей на основе ADT в реализациях поведения (функциях)
Деструктуризация ADT с помощью сопоставления с образцом
Дублирование кода и правило DRY
Практика сопоставления с образцом
Новые типы, ADT и сопоставление с образцом в дикой природе
Что можно сказать о наследовании
Перерыв на кофе: проектирование функциональных данных
Объяснение для перерыва на кофе: дизайн функциональных данных
Моделирование поведения
Моделирование поведения как данных
Реализация функций с параметрами на основе ADT
Перерыв на кофе: проектирование и удобство сопровождения
Объяснение для перерыва на кофе: проектирование и удобство сопровождения
Резюме
8. Ввод-вывод как значения
Общение с внешним миром
Интеграция с внешним API
Свойства операции ввода-вывода с побочным эффектом
Императивное решение для кода ввода-вывода с побочными эффектами
Проблемы императивного подхода к вводу-выводу
Позволит ли ФП добиться большего успеха
Ввод-вывод и использование его результата
Императивный ввод-вывод
Вычисления как значения IO
Значения IO
Значения IOв реальной жизни
Удаляем загрязнения
Использование значений, полученных из двух операций ввода-вывода
Объединение двух значений IO в одно
Практика создания и объединения значений IO
Разделение задач при работе только со значениями
Тип IO — вирусный
Перерыв на кофе: работа со значениями
Объяснение для перерыва на кофе: работа со значениями
На пути к функциональному вводу-выводу
Как быть со сбоями ввода-вывода
Программа, описываемая значением IO, может завершиться неудачей!
Помните orElse?
Отложенные и немедленные вычисления
Реализация стратегий восстановления с использованием IO.orElse
Реализация запасных вариантов с использованием orElse и pure
Практика восстановления после сбоев в значениях IO
Где должны обрабатываться потенциальные сбои
На пути к функциональному вводу-выводу с обработкой сбоев
Чистые функции не лгут даже в небезопасном мире!
Функциональная архитектура
Использование IO для сохранения данных
Перерыв на кофе: использование IO для сохранения данных
Объяснение для перерыва на кофе: использование IO для сохранения данных
Интерпретация всего как значений
Интерпретация повторных попыток как значений
Интерпретация неизвестного количества вызовов API как значения
Практика восприятия функциональных сигнатур
Резюме
9
9. Потоки данных как значения
Бесконечность не предел
Работа с неизвестным количеством значений
Работа с внешними нечистыми вызовами API (снова)
Функциональный подход к проектированию
Неизменяемые ассоциативные массивы
Практика неизменяемых ассоциативных массивов
Сколько вызовов IO следует сделать
Проектирование снизу вверх
Расширенные операции со списком
Знакомство с кортежами
Упаковка и отбрасывание
Сопоставление с образцом для кортежей
Перерыв на кофе: ассоциативные массивы и кортежи
Объяснение к перерыву на кофе: ассоциативные массивы и кортежи
Функциональные пазлы
Следование за типами в восходящем проектировании
Прототипирование и тупики
Рекурсивные функции
Бесконечность и ленивые вычисления
Структура рекурсивной функции
Обработка отсутствия значения в будущем (с использованием рекурсии)
Полезность бесконечных рекурсивных вызовов
Перерыв на кофе: рекурсия и бесконечность
Объяснение для перерыва на кофе: рекурсия и бесконечность
Создание различных программ IO с использованием рекурсии
Использование рекурсии для выполнения произвольного количества вызовов
Проблемы рекурсивной версии
Потоки данных
Потоки данных в императивных языках
Вычисление значений по требованию
Потоковая обработка, производители и потребители
Типы Stream и IO
Функциональный тип Stream
Потоки в ФП — это значения
Потоки — это рекурсивные значения
Примитивные операции и комбинаторы
Потоки значений IO
Бесконечные потоки значений IO
Выполнение ради побочных эффектов
Практика работы с потоками
Использование преимуществ потоков
Бесконечный поток вызовов API
Обработка ошибок ввода-вывода в потоках
Разделение ответственности
Скользящие окна
Ожидание между вызовами ввода-вывода
Упаковка потоков
Преимущества потоковой обработки
Резюме
Часть IIIПрикладное функциональное программирование
11. Разработка функциональных программ
Заставьте это работать, заставьте работать правильно, заставьте работать быстро
Моделирование с использованием неизменяемых значений
Моделирование предметной области и ФП
Моделирование доступа к данным
Мешок функций
Бизнес-логика как чистая функция
Отделение задачи доступа к данным
Интеграция с API с применением императивных библиотек и IO
Следуя проекту
Реализация действий ввода в виде значений IO
Отделение библиотеки ввода-вывода от других задач
Каррирование и инверсия управления
Функции как значения
Связываем все вместе
Мы заставили решение работать
Заставляем работать правильно
Утечки ресурсов
Управление ресурсами
Использование значения Resource
Мы заставили работать правильно
Перерыв на кофе: заставьте работать быстро
Объяснение для перерыва на кофе: заставьте работать быстро
Резюме
12. Тестирование функциональных программ
У вас есть тесты?
Тесты — это просто функции
Выбор функций для тестирования
Тестирование на примерах
Практика тестирования на примерах
Создание хороших примеров
Генерирование свойств
Тестирование на основе свойств
Тестирование путем предоставления свойств
Делегирование работы путем передачи функций
Интерпретация сбоев тестов на основе свойств
Ошибка в тесте или в программе?
Нестандартные генераторы
Тестирование более сложных случаев в удобочитаемой форме
Поиск и исправление ошибок в реализации
Перерыв на кофе: тесты на основе свойств
Объяснение для перерыва на кофе: тесты на основе свойств
Свойства и примеры
Охват требований
Тестирование требований с побочными эффектами
Определение правильного теста для работы
Тесты для проверки использования данных
Практика имитации внешних сервисов с использованием IO
Тестирование и дизайн
Тесты для проверки интеграции с сервисами
Локальные серверы как значения Resource в интеграционных тестах
Написание изолированных интеграционных тестов
Интеграция с сервисом — единая ответственность
Перерыв на кофе: написание интеграционных тестов
Объяснение для перерыва на кофе: написание интеграционных тестов
Интеграционные тесты выполняются дольше
Интеграционные тесты на основе свойств
Выбор правильного подхода к тестированию
Разработка через тестирование
Написание теста для несуществующей функции
Красный, зеленый, рефакторинг
Делаем тесты зелеными
Добавление красных тестов
Последняя итерация TDD
Резюме
Последний танец
Приложение. Памятка по Scala
Определение значения
Определение функции
Вызов функции
Создание неизменяемых коллекций
Передача функции по имени
Передача анонимной функции
Передача анонимной функции с двумя параметрами
Определение функций с несколькими списками параметров (каррирование)
Объект Math
Определение case-класса (типа-произведения) и создание его значения
Точечный синтаксис для доступа к значениям в case-классе
Синтаксис определения анонимных функций с символом подчеркивания
Отсутствующая реализация: ???
Интерполяция строк
Передача многострочной функции
Автоматическое определение типов и пустые списки
Автоматическое определение типа и форсирование типа
Определение for-выражения
Объекты как модули и объект как «мешки» типов и функций
Определение непрозрачного типа (newtype)
Импорт всего из объекта с использованием синтаксиса подчеркивания
Создание и использование значения непрозрачного типа
Определение перечислений (типов-сумм)
Сопоставление с образцом
Именование параметров в конструкторах и функциях классов
Использование интерфейсов trait для определения пакетов функций
Создание экземпляров интерфейсов trait (пакетов функций)
Значение Unit в Scala
Создание неизменяемого типа Map
Передача функций, соответствующих образцу
Игнорирование значения с помощью символа подчеркивания
Протяженность интервалов времени и большие числа
Приложение. Жемчужины функционального программирования