Функциональное программирование: код без проблем и ошибок
Функциональное программирование (FP) долгое время оставалось нишевым явлением. Однако теперь многие языки (Java и Python в их числе) все чаще стали перенимать его концепции.
Самая важная задача FP — сделать код понятнее. Такой тип программирования по своей природе прекрасно подходит для машинного обучения и анализа данных.
Но это совсем не означает, что есть смысл осуществления полного перехода с объектно-ориентированного программирования на функциональное.
Функциональное программирование VS объектно-ориентированное программирование
Функциональное программирование | Объектно-ориентированное программирование |
Использование неизменяемых данных. | Использование изменяемых данных. |
В функциях нет побочных эффектов. | Может иметь большое количество побочных эффектов. |
Порядок выполнения функций не имеет значения. | Порядок выполнения имеет значения. |
Поддержка абстракции данных и абстракции поведения. | Поддержка абстракции данных. |
Для управления потоком используются вызовы функций и вызовы функций с рекурсией. | Для управления потоком используются условные операторы и циклы. |
Поддержка параллелизма. | Без поддержки параллелизма. |
Осуществляется в рамках декларативного программирования. | Осуществляется в рамках императивного программирования. |
Смысл FP заключается в описании взаимодействий между командами и подпрограммами. Здесь не задаются последовательности этих команд, а весь код состоит из правил работы с данными.
Функциональное программирование:
- использует неизменные данные;
- основано на математических функциях, где для выполнения вычислений используются условные выражения и рекурсия;
- не поддерживает итерации;
- делает главный акцент на вычислениях.
Чистые функции
Функциональное программирование — это способ создания программы с использованием так называемых чистых функций.
Чистая функция — это функция, которая связывает входные значения с выходными и не содержит больше никаких сторонних вычислений. Она зависит только от собственных параметров и не зависит от контекста, не изменяет переменные, а создает новые на выходе.
Функцию можно назвать чистой, если:
- при ее выполнении не появляются побочные эффекты (скрытый ввод-вывод или неявные входы-выходы);
- она всегда отдает одно значение при одних и тех же вызываемых аргументах.
Почему чистые функции в приоритете?
- Не изменяют структуру данных.
- Имеют прозрачные ссылки, а потому могут быть заменены без изменений в поведении самой программы.
- Возможность композиции — объединения нескольких функций в одну результирующую.
- Результатом чистых функций является возвращаемое значение.
Функции первого класса и высшего порядка
Обязательное требование в функциональном программировании — использование функций первого класса и высшего порядка.
Функции первого класса являются переменными первого класса. Эти переменные передаются функциям в виде параметров. Они сохраняются в структуре данных или могут быть возвращены из функций.
Функции высшего порядка также могут возвращать функции или принимать другие в качестве аргументов.
Например:
show_output(f) // function show_output is declared taking argument f // which are another function f(); // calling passed function print_gfg() // declaring another function print("hello gfg"); show_output(print_gfg) // passing function in another function
Концепция высшего порядка применяется к функциям в целом, а функции первого порядка касаются функций в языках программирования.
Часто функции высшего порядка применяются для:
- асинхронного управления потоком с помощью функций обратного вызова;
- частичного применения функции к ее аргументам с целью повторного применения;
- создания утилит, которые могут работать с различными типами данных.
Неизменяемые переменные
Функциональное программирование не предусматривает изменение переменных после инициализации. Несмотря на то, что у нас есть возможность создания новых переменных, уже существующие изменить нельзя. Такой подход помогает поддерживать состояние программы в течение всего времени ее выполнения. Установив значение вновь созданной переменной, можно быть уверенным, что ее значение уже никогда не изменится.
Без неизменяемой концепции поток данных в программе может испытывать потери, и как следствие, могут возникать ошибки.
Неизменяемость полезна для потокобезопасности кода (thread-safety).
Ссылочная прозрачность
Относительная прозрачность функций означает, что выражение может быть заменено значением без критических изменений в самой программе. Это значит, что методы всегда возвращают одно и то же значение для конкретного аргумента и не оказывают никакого иного влияния.
Ссылочная прозрачность имеет важное значение, поскольку поддается кэшированию.
Например: пусть создаваемая функция square() возвращает результат умножения числа самого на себя:
function square(n) { return n * n; }
Функция является чистой, а потому для одного и того же значения на входе возвращает одно и то же значение на выходе:
square(2); // 4 square(2); // 4 square(2); // 4 // ...
Сколько бы функции не передавалось 2, она будет всегда возвращать число 4. Получается, что square(2) можно заменить 4 — это означает, что функция имеет ссылочную прозрачность.
Функциональное программирование и лямбда-исчисления
Важно отметить, что функциональное программирование основано на лямбда-исчислении. Если углубиться и понять принципы лямбда-исчисления, можно получить много интуитивных представлений о том, как может применяться на практике функциональное программирование.
Лямбда-исчисление состоит из трех элементов:
- функции;
- переменные;
- применение функций к переменным.
Функции являются чистыми: отображения элементов набора входов на элементы набора выходов. Благодаря этому мы можем кодировать любые вычисления и создавать различные функции.
Принципы лямбда-исчисления, формирующие функциональное программирование:
- Прохождение всех функций через процесс каррирования — выполнение функции с несколькими аргументами. Сначала — с первым аргументом, в результате чего будет возвращена новая функция с набором аргументов на один меньше. Рекурсия (повторение) будет продолжаться, пока не будут использованы все аргументы функции.
- Все функции являются анонимными, значение имеет список аргументов.
В заключение
Хотя разобраться в функциональном программировании не так уж и просто, его популярность набирает обороты.
Преимущества функционального программирования
- Простое тестирование и отладка. Это возможно благодаря чистым функциям, которые используют неизменяемые значения, принимают аргументы и осуществляют выводы. С их помощью становится намного проще разрешать проблемы, возникающие в программе.
- Ленивая оценка. Значение оценивается и сохраняется только в случае необходимости. Нет необходимости полной повторной оценки.
- Применение и реализация параллелизма. Функциональное программирование полезно для реализации параллелизма. Это возможно благодаря чистым функциям, которые не вносят никаких изменений в данные и переменные.
- Способность обрабатывать функции как значения. Значения передаются функциям в качестве параметров — от такой реализации код становится читаемым, простым и понятным.
- Простота понимания чистых функций. Чистые функции зависят только от входных данных, они не меняют значения и состояния программы. Каким бы ни был результат, они возвращают изначальное значение.
- Ориентирование на результаты, а не процесс. Важный момент в программировании — получение удобоваримого конечного результата.
- Неизменяемые данные. Это значит, что можно легко создавать структуры данных и при этом не изменять уже существующие.
Недостатки функционального программирования
- Объединение чистых функций с другими операциями ввода-вывода.
- Неизменяемость и рекурсия иногда могут приводить к снижению производительности.
- Рекурсия, в отличие от метода написание программ при помощи циклов, не слишком распространена, оттого может пугать и отталкивать.
- Многие объекты создаются в момент кодирования, потому его сложно поддерживать.
Функциональное программирование позволяет избавиться от проблем и ошибок в коде, эффективно использует лямбда-исчисление, поддерживает вложенные функции. Оно предлагает большие возможности модульности при меньшем количестве строк кода, тем самым увеличивая продуктивность разработчика.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: