Введение в функциональное программирование #
Васильев Андрей Михайлович, 2022
Версии презентации
Краткий обзор курса #
Курс основан на книге Grokking Simplicity Эрика Норманда
- Официальная страница книги: https://www.manning.com/books/grokking-simplicity
- Подкаст автора: Thoughts on Functional Programming
В рамках книги автор пытается внести определение функциональному программированию и разобрать базовые техники программирования
Необходимые знания для прохождения курса:
- Базовое знание языка JavaScript
- Понимание процесса выполнения JavaScript в рамках браузера
Императивное и декларативное программирование #
- При использовании императивного подхода необходимо написать все шаги алгоритма для достижения цели
- При использовании декларативного подхода программист описывает финальное состояние, которое хочет достичь
Пример императивного подхода #
Функция для вычисления суммы чисел на JavaScript
function sum(array) {
let result = 0;
for(let i = 0; i < array.length; i++) {
result += array[i];
}
return result;
}
Примеры императивных языков программирования #
Большинство языков общего назначения являются императивными: C, C++, Python, C#, Java, JavaScript, Ruby, Kotlin, Swift, PHP, Go
С их помощью мы можем описать произвольные алгоритмы для решения любых задач
Пример декларативного подхода #
Выборка данных из базы данных на языке SQL
select name,surname from students where group='IT-11-MO'
Примеры декларативных языков программирования #
Большинство предметно-ориентированных языков являются декларативными: SQL, HTML, XML, Prolog, QWL, XSLT, SPARQL
Данные языки реализованы зачастую на основе императивных языков программирования
Смешение подходов #
Программисты заметили, что для решения задач в конкретной предметной области объём кода на декларативном языке заметно меньше кода на императивном языке. А чем меньше понятного кода написано, тем легче понимать данный код, тем легче его поддерживать.
Варианты внедрения декларативного подхода:
- Некоторые языки программирования позволяют формировать предметно-ориентированные языки (domain specific languages, DSL) для решения поставленных задач наиболее кратким и удобным образом, например Ruby
- Создание смешанных языков: QML (язык описания интерфейсов в Qt)
- Внедрение техник функционального программирования: C++, C#, Python, JavaScript
Структурное, объектно-ориентированное #
Все данные языки являются императивными языками программирования
Структурное и процедурное программирование #
Исторически один из первых подходов к формированию программ, включает:
- Последовательность действий, выполняемых друг за другом
- Использование условий и циклов
- Использование процедур для повторного использования кода
- Описание собственных типов данных, структур
Объектно-ориентированное программирование #
Ядром данной концепции является объект, который включает в себя данные и код, который обрабатывает эти данные
- Обычно доступно большинство концепций структурного программирования
- Большинство языков основывает систему объектов на классах или прототипах
- Наследование, инкапсуляция, полиморфизм
Определение функционального программирования #
Рассмотрим типичные определения функционального программирования
- парадигма программирования, характеризуемая использованием математических функций и избегания побочных эффектов
- стиль программирования, который использует чистые функции без побочных эффектов
Побочные эффекты #
Ими называют любые действия функции, за исключением возвращения данных. В эти действия входит печать данных на экране, отправка почтового сообщения или модификация глобального состояния.
Побочные эффекты могут стать проблемой, так как они происходят при каждом вызове функции и могут быть непреднамеренными
Чистые функции #
Данные функции только лишь вычисляют результирующее значение на основе аргументов, которые были ей переданы, не имеет никаких побочных эффектов
- Не зависит от времени вызова, т.е. на каждый набор аргументов всегда будет возвращено одно результирующее значение
- Их сравнивают с математическими функциями, т.к. именно так функции работают в математике
В функциональном программировании приветствуется использование чистых функций, так как их легче понять и ими легче управлять
Неверная интерпретация определения ФП #
ФП — парадигма программирования при которой используются исключительно функции без побочных эффектов
Проблемы определений ФП #
Программам нужны побочные эффекты #
Из определения следует, что побочных эффектов следует избегать, но они являются причиной существования ПО: почтовому клиенту нужно посылать электронные письма
ФЯП удобны для работы с побочными эффектами #
В функциональных ЯП разработаны специальные техники для работы с побочными эффектами, чтобы их можно было совмещать с чистыми функциями
ФЯП практичны #
Определение парадигмы посредством математики может сказать, что парадигма неприменима на практике. На самом деле на ФЯП можно реализовать любое приложение и множество существующих проектов являются доказательством
ФП как набор навыков и концепций #
В рамках данного курса (и базовой книги) мы попытаемся получить навыки и понять концепции ФП, которые можно применять уже сейчас. Причём многие из этих навыков применимы за пределами чисто функциональных языков программирования
Многие императивные языки вобрали в себя инструменты работы с данными из функциональных языков
Различие действий, вычислений и данных #
С точки зрения ФП код можно разделить на 3 важных категории
Действия #
Действия — это функции, результат работы которых зависит от времени их запуска или от количества запусков
То есть под действиями мы понимаем функции с побочными эффектами
Примеры таких функций:
sendEmail(to, from, subject, body)
— отправляет почтовое сообщениеsaveUserDB(user)
— после сохранения данные будут доступны всей системеgetCurrentTime()
— каждый вызов данной функции вернёт новые данные
Вычисления и данные #
Вычисления и данные не зависят ни от времени обращения, ни от количества обращений. При любом обращении эти элементы будут возвращать корректные данные
Примеры:
{"firstname": "Bob"}
— данные не изменяются со временем (самостоятельно)sum(numbers)
— сумма чисел зависит только от переданных чиселstring_length(string)
— длина зависит от строки[1, 10, 15, 5, 26]
— массив не изменяется со временем
Разделяем вычисления и данные #
Разница между вычислениями и данными заключается в том, что вычисления могут быть выполнены, а данные нет
- Данные нейтральны, прозрачны для восприятия
- Результат вычислений не понятен до его окончания
Работа с категориями в ФЯП #
При работе над исходным кодом программисты на ФЯП различают все три категории
- Все категории используются при написании приложений на ФЯП
- У каждой категории есть свои плюсы и минусы
- Данные лучше вычислений, вычисления лучше действий
Определение категорий #
Рассмотрим следующий пример работы веб-приложения:
- Пользователь помечает в интерфейсе задачу как выполненную
- Веб-браузер посылает сообщение на сервер, описывающий действие пользователя
- Сервер получает сообщение
- Сервер обрабатывает данные и сохраняет их в базу данных
- Сервер выбирает корреспондентов для отправки почтовых сообщений
- Сервер высылает сообщение выбранным корреспондентам
Определим для каждого шага его категорию
Определение категорий #
-
Пользователь помечает в интерфейсе задачу как выполненную
Это действие, т.к. оно зависит от количества вызовов
-
Веб-браузер посылает сообщение на сервер, описывающий действие пользователя
Сообщение является данными, но их отправка — это действие
-
Сервер получает сообщение
Получение сообщения — это действие, т.к. зависит от количества
-
Сервер обрабатывает данные и сохраняет их в базу данных
Изменение состояния БД — это действие
-
Сервер выбирает корреспондентов для отправки почтовых сообщений
Выбор корреспондентов зависит только от события, это вычисление
-
Сервер высылает сообщение выбранным корреспондентам
Отправка почты — это действие
Свойства трёх категорий исходного кода #
Действия #
Если код зависит от времени выполнения, количества запусков или того и другого, то это действие
- Результат отправки срочного почтового сообщения сегодня или через неделю важно
- Отправка сообщения один или десять раз важно
Вычисления #
Если код занимается исключительно формированием результата на основании аргументов, то это вычисление
- При передаче одинаковых аргументов возвращает одинаковый результат
- Результат вычисления не зависит от времени вызова или количества вызовов
- В результате их работы не происходит изменений вне данной функции
Данные #
Данные обычно представляют собой записанные факты о произошедших событиях
- Данные имеют смысл без выполнения
- Данные можно интерпретировать различными способами
Рассмотрим чек из ресторана. На данных из чека можно
- Менеджеру ресторана можно узнать какие блюда являются популярными
- Покупателю можно отслеживать свои траты
Инструменты ФЯП #
Действия #
- Средства для безопасного изменения состояния во времени
- Подходы для гарантии последовательных вычислений
- Инструменты для выполнения действий только один раз
Вычисления #
- Средства статического анализа для достижения корректности
- Возможность применения математического аппарата
- Эффективность применения автоматического тестирования
Данные #
- Подходы для организации эффективного доступа к данным
- Дисциплины для сохранения данных на долгую перспективу
- Принципы для сохранения важной информации в данных
Зачем заниматься категоризацией? #
ФЯП по своей природе гораздо лучше работают в рамках распределённых систем по сравнению с другими языками
Большинство современных приложений — распределённые
- Веб-приложения по определению общаются с сервером
- Мобильные приложения зачастую завязаны на сервер
- Серверные приложения сами по себе состоят из множества компонент
Проблемы сетевых распределённых приложений:
- Сообщения могут придти не в правильном порядке
- Сообщения могут дублироваться
- Сообщения могут не доходить
- Ответ на сообщение может тоже не дойти до отправляющей стороны
Т.е. возникают проблемы, связанные с моделированием изменений во времени: проблемы от количества вызовов и от времени вызовов
Чем категории могут помочь? #
Данные и вычисления не зависят от количества обращений, ни от времени их выполнения. Чем больше данных и вычислений в приложении и чем меньше действий, тем меньше мест в приложении, где может возникнуть проблемы распределённых приложений
Проблемы останутся в действиях, но мы их явно идентифицировали в приложении, поэтому можем применить к ним техники управления указанными проблемами
Что такое функциональное мышление #
Функциональное мышление — это набор умений и идей, которые используются при написании приложений на функциональных языках программирования
В рамках курса (и книги) постараемся овладеть этими инструментами, чтобы успешно применять их на практике
Базовые идеи ФП:
- Разделение всего кода на действия, вычисления и данные
- Использование функциональных абстракций и подходов
Правила выбора идей и навыков #
Данные правила были использованы для выбора идей для книги, и следовательно будут рассмотены в курсе
- Они не должны быть основаны на уникальных особенностях функциональных языков программирования. Пример: мощная система типов, встроенные неизменяемые типы и т.п.
- Навыки должны быть применимы на практике и приносить пользу при программировании
- Идеи и навыки должны быть применимы к коду в любой ситуации, не обязательно начинать новый проект на ФЯП, чтобы применить эти навыки и идеи