ВЛИЯНИЕ ФУНКЦИОНАЛЬНОГО ПРОГРАММИРОВАНИЯ НА СОВРЕМЕННЫЕ ЯЗЫКИ ПРОГРАММИРОВАНИЯ
Введение 
Как известно, функциональное программирование (далее – ФП) – одна из первых парадигм, нацеленных не просто на получение эффективной реализации заранее подготовленных алгоритмов, а на решение новых задач информационной обработки, обладающих исследовательским компонентом. 
Функции являются фундаментальными для организации кода; они существуют во всех языках программирования более высокого порядка. Как правило, функциональное программирование означает использование функций с наилучшим эффектом для создания чистого и поддерживаемого программного обеспечения. Более конкретно, функциональное программирование – это набор подходов к кодированию, обычно описываемых как парадигма программирования. 
Функциональное программирование иногда определяется в противовес объектно-ориентированному программированию (ООП) и процедурному программированию. Это вводит в заблуждение, поскольку эти подходы не являются взаимоисключающими, и большинство систем, как правило, используют все три. 
Функциональное программирование предлагает очевидные преимущества в определенных случаях, оно широко используется во многих языках и фреймворках и занимает видное место в современных тенденциях программного обеспечения. Это полезный и мощный инструмент, который должен быть частью концептуального и синтаксического инструментария каждого разработчика.  
1. Функции современных языков ФП 
1.1. Функции первого класса 
Следует согласиться с мнением А.Э. Романовой о том, что язык программирования – это формальный язык, который необходим для написания разного рода компьютерных приложений, служб и драйверов [1]. Все языки программирования состоят из определенных компонентов и функций.  
Отличительной чертой функционального программирования, в целом, является широкое использование функций, которые превратились в основной инструмент разработки. С нашей точки зрения, следует для начала рассмотреть основные определения, описывающие различия между функциями и процедурами, а также другие подобные конструкции нефункциональных языков.
Так, к примеру, функции высшего порядка – это действия, которые либо принимает другую функцию в качестве аргумента, либо возвращает некоторую функцию в качестве результата. Их также называют функционалами. Такое поведение может быть реализовано даже в чистом C с помощью указателей на функции (см. рис. 1).
Рис. 1. Функции высшего порядка в чистом C
Функции первого класса это те, которыми можно манипулировать таким же образом, как и любым другим значением: передавать в качестве аргументов, возвращать в качестве результатов, присваивать переменным и структурировать поля. 
Лямбда-функция – это анонимная функция. Помимо отсутствия имени, поддержка лямбда-функций устраняет другие языковые ограничения на объявления функций (в некоторых языках, например, в стандарте C99, объявления функций могут выполняться только на более высоком уровне). Поддержка лямбда-функций подразумевает, что функция может быть объявлена в любом месте, где допустимы другие выражения. Лямбда-функции в основном используются в функциях более высокого порядка; при использовании в сочетании они обеспечивают удобство и значительное сокращение кода. 
Замыканиe (closure) – это функция, которая может захватывать некоторые переменные из контекста, в котором она была объявлена, не позволяя сборщику мусора удалять данные, которые могут быть использованы в этой функции, при условии, что приложение имеет ссылку на саму функцию. 
1.2. Понимание списка и сопоставление шаблонов 
Понимание списков позволяет кратко описать обработку или генерацию списков с использованием существующих. Miranda был одним из первых языков, использующих такой синтаксис, который был принят Haskell; позже подобные конструкции появились в “менее функциональных” языках, таких как Python, C # и Ruby [4]. 
Отметим, что в настоящее время, Haskell является лидером по внедрению новых функций.  
Алгебраические типы данных. Эти типы также можно назвать ADT, типами сумм, дискриминационными объединениями, дизъюнктивными объединениями, копродуктами и, возможно, некоторыми другими умными терминами. Возможно, используется другое название для таких типов. В двух словах, это составной тип, содержащий дискриминантное поле (которое можно назвать тегом) вместе со связанными данными. Один пример в Haskell такого составного типа описывает возможные действия пользователя в гипотетической реализации приложения TodoMVC. Некоторые действия связаны с “полезной нагрузкой” (строка или идентификатор элемента) (см. рис. 2). 
Несмотря на его простоту и полезность для моделирования объектов предметной области, ADT редко поддерживаются в полном масштабе в популярных языках и базах данных. Вот несколько примеров, реализующих похожие типы: Enum в Rust, закрытые классы в Kotlin, std: variant в C++.
Рис. 2. Возможные действия пользователя в гипотетической реализации приложения TodoMVC 
Сопоставление с образцом – это синтаксическая конструкция, которая предоставляет доступ к данным структуры, состоящей из одной или нескольких альтернатив с разными наборами полей (тот самый ADT, алгебраический тип sum, enum, std: variant и т.д). Сопоставление шаблонов напоминает оператор переключения регистров, который все знают из императивных языков. Однако его главное преимущество заключается в том, что компилятор проверяет доступ к альтернативным полям статически, используя информацию о типе выражения, в то время как switch-case не предотвращает ошибки, связанные с неправильным доступом к полям, пропущенными регистрами или избыточными проверками. 
Сопоставление с образцом – это еще один метод, популяризированный в функциональных языках, где он оказался весьма полезным. Сейчас оно принято и интегрировано – в различных формах – в Python, Java, C #, Rust и другие популярные языки. 
2. Использование ленивой оценки в ФП 
2.2. Ленивая оценка  
Отметим, что в большинстве языков программирования выполняется оценка. Одной из причин популярности lodash является его отличная производительность, большая часть которой основана на алгоритме ленивой оценки. Так, в большинстве языков программирования оценка выполняется, когда переменной присваивается значение; все аргументы оцениваются перед вызовом функции (строгие оценки). Альтернативный подход – «ленивый», подразумевающий, что вычисление откладывается до тех пор, пока значение не будет фактически использовано. Отложенная оценка позволяет работать с бесконечными структурами данных, писать декларативный код с определениями, расположенными в порядке, удобном для чтения, вместо порядка их оценки. Если используется подход DSL, отложенные вычисления помогают легко реализовать такие конструкции, как if-then-else (где значения будут оцениваться только в нужной вам ветке) [3]. 
Историю термина можно проследить до лямбда-исчисления, составляющего часть теоретических основ функционального программирования, поэтому неудивительно, что он используется в языках ФП. Например, в Haskell по умолчанию все вычисляется лениво. 
Некоторые элементы «лени» можно найти в других языках. Даже в чистом C операторы && и || ленивы, поскольку они не вычисляют свой второй аргумент, если первый был соответственно оценен как 0 или 1. В языках более высокого уровня более распространенным термином является «отложенные оценки», которые реализуются с использованием функций генератора и ключевого слова «yield». Такие генераторы можно найти, например, в Python и Java. 
Продолжение – это «оставшаяся оценка», то есть описание того, что должно быть сделано с результатом выражения для каждого подвыражения в программе. Выражение получает свое продолжение в виде дополнительного аргумента, и когда результат получен, текущая функция вызывает переданное продолжение с вычисленным значением вместо того, чтобы возвращать результат напрямую. Такой стиль передачи результата называется стилем продолжения передачи (CPS). 
CPS редко встречается непосредственно в исходном коде программного обеспечения. Компиляторы являются одной из основных областей применения CPS – в качестве промежуточного представления перед генерацией машинного кода. Преобразование кода в CPS позволяет преобразовать рекурсивные вызовы функций в хвостовую рекурсию, которую можно легко оптимизировать, чтобы избежать роста стека во время вычислений. 
Продолжения – это очень мощный инструмент, который можно использовать для реализации управляющих структур, таких как ранний выход функции, явный вызов хвостовой рекурсии, императивные циклы и т.д.
2. Будущее (Future) и обещания (Promise, Deferred) 
Фьючерсы, также известные как Promises и Deferred, представляют собой конструкцию, содержащую оценку асинхрон ных значений. Они появились в функциональном программировании как инструмент, используемый для упрощения параллельных вычислений и выполнения запросов в распределенных системах [2]. 
Promises были популяризированы главным образом из-за их адаптации и широкого использования в браузере. Выполнение JavaScript в браузере ограничено только одним потоком выполнения. Следовательно, блокировка во время ожидания HTTP-ответа, как и в случае с большинством платформ, привела бы к зависанию страницы и раздражению пользователей. Вот почему функции обратного вызова используются для обработки ответов на HTTP-запросы в браузере. В то же время комбинировать такие запросы не очень удобно, и появился термин «Ад обратного вызова» для описания кода, который стал неразборчивым из-за большого количества обратных вызовов. Обещания позволили частично решить проблему отправки параллельных запросов и последовательных цепочек запросов. 
Так, во многих популярных языках (таких как C#, Java, JavaScript) обещания стали основным инструментом асинхронного программирования. 
2.3. Монадический интерфейс и асинхронность 
Названия многих конструкций и методов программирования в Haskell были заимствованы из теории категорий и других разделов математики. Один из таких терминов – Монада – стал объектом многих мемов и шуток о функциональном программировании. В Интернете есть много сообщений, объясняющих, что такое «Монады» в функциональных языках и как их использовать [5]. 
Проще говоря, «monad» – это просто интерфейс с двумя методами, которые позволяют объединять оценки в цепочку таким же образом, как это сделано в примере с цепочкой обещаний. Сами обещания также иллюстрируют реализацию монадического интерфейса. 
Среди других применений монад в чисто функциональных языках, таких как Haskell, есть инкапсуляция побочного эффекта. Поскольку на этих языках невозможно выполнить запрос к базе данных, прочитать файл или даже напечатать строку в стандартном выводе путем вызова обычных функций, для выполнения этих действий используются монады. В то же время, их применение выходит за рамки побочных эффектов – монадический интерфейс универсален и позволяет писать общий, лаконичный код высокого уровня, именно поэтому монады используются в Haskell повсеместно. Помимо Haskell, сами монады используются не так широко, но их влияние можно увидеть в первую очередь в программировании с использованием promises и в конструкции async-await. 
Возвращаясь к примерам кода с promises, можно заметить, что, несмотря на все преимущества promises, цепочки вызовов выглядят ничуть не лучше обратных вызовов. Синтаксическая структура async-await позволяет сделать еще один шаг вперед и улучшить код с помощью цепочки обещаний, превратив его во что-то очень похожее на некоторый код с блокировкой вызовов. 
Появление async-await можно проследить до исследования, посвященного функциональному программированию в Haskell и ML, которое привело к появлению асинхронных рабочих процессов в F # (2007), а затем в C # (2011). 
Заключение 
Вышесказанное позволяет сделать объективное заключение о том, что в современном мире функциональное программирование переживает «ренессанс» из-за того, что функциональные языки позволяют писать декларативный, понятный и надежный программный код.
В C# были добавлены lambda выражения, был создан LINQ framework, который позволяет делать запросы к объектам, базам данных, строя цепочки вызовов. В языках программирования Java, C++, Python также были добавлены lambda выражения. Из этого следует вывод, что тренд на изучение и имплементацию концепций функциональной парадигмы существует в современных языках программирования общего пользования, что делает большинство современных языков программирования – мультипарадигмальными. 
Таким образом, языки программирования движутся вперед и активно развиваются, накапливая новые, все более совершенные и удобные инструменты. Как вы можете видеть, популярные языки, такие как Python и С ++, приобретают все большее количество функций, которые произошли от функционального программирования.