> For the complete documentation index, see [llms.txt](https://folko.gitbook.io/podgotovka-k-sobesedovaniyu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://folko.gitbook.io/podgotovka-k-sobesedovaniyu/yazyki-programmirovaniya/funkcionalnoe-programmirovanie.md).

# Функциональное программирование

**Функциона́льное программи́рование** — [парадигма программирования](https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%B8%D0%B3%D0%BC%D0%B0_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F), в которой процесс [вычисления](https://ru.wikipedia.org/wiki/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5) трактуется как вычисление значений [функций](https://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F_\(%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0\)) в математическом понимании последних (в отличие от [функций](https://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F_\(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\)) как подпрограмм в [процедурном программировании](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)).

Противопоставляется парадигме [императивного программирования](https://ru.wikipedia.org/wiki/%D0%98%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5), которая описывает процесс вычислений как последовательное изменение [состояний](https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5) (в значении, подобном таковому в [теории автоматов](https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%BE%D0%B2)). При необходимости, в функциональном программировании вся совокупность последовательных состояний вычислительного процесса представляется явным образом, например, как [список](https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_\(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0\)).

Функциональное программирование предполагает обходиться вычислением результатов функций от исходных данных и результатов других функций, и не предполагает явного хранения состояния программы. Соответственно, не предполагает оно и изменяемость этого состояния (в отличие от [императивного](https://ru.wikipedia.org/wiki/%D0%98%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5), где одной из базовых концепций является [переменная](https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F_\(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\)), хранящая своё значение и позволяющая менять его по мере выполнения [алгоритма](https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC)).

На практике отличие математической функции от понятия «функции» в императивном программировании заключается в том, что императивные функции могут опираться не только на аргументы, но и на состояние внешних по отношению к функции переменных, а также иметь [побочные эффекты](https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B1%D0%BE%D1%87%D0%BD%D1%8B%D0%B9_%D1%8D%D1%84%D1%84%D0%B5%D0%BA%D1%82_\(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\)) и менять состояние внешних переменных. Таким образом, в императивном программировании при вызове одной и той же функции с одинаковыми параметрами, но на разных этапах выполнения алгоритма, можно получить разные данные на выходе из-за влияния на функцию состояния переменных. А в функциональном языке при вызове функции с одними и теми же аргументами мы всегда получим одинаковый результат: выходные данные зависят только от входных.&#x20;

\
**Плюсы:**

1. Код станет более лаконичным и выразительным. «Выразительность» можно определить как количество идей на единицу кода, и в целом функциональные языки, будучи более высокоуровневыми, оказываются и более выразительными. Например, преобразование каждого элемента в массиве или списке реализуется функциональным однострочником (используя map/foreach/whatever и анонимную функцию), в то время как в императивном стиле пришлось бы организовывать цикл, объявлять переменную для счётчика или итератора и использовать явное присваивание.&#x20;
2. Декомпозиция кода будет происходить более естественно. Принцип «Разделяй и властвуй» уже прочно закрепился в разработке и является базовым принципом борьбы со сложностью ПО. В функциональном программировании эти мелкие задачи выражаются как небольшие вспомогательные функции, каждая из которых делает своё дело и её работу можно описать одним коротким предложением. А построение итогового результата — это композиция таких функций. Конечно, разбить код на отдельные переиспользуемые части можно и в ООП, и в чисто императивном низкоуровневом языке типа C, и для этого уже есть известные принципы типа SOLID и GoF-паттерны, но, когда сам язык заставляет программиста думать в терминах функций, декомпозиция кода происходит гораздо более естественно.\
   ![](https://habrastorage.org/webt/s_/zs/ff/s_zsffvdfjxwoyxdknf_xn1ahd4.jpeg)<br>
3. Побочные эффекты будут отделены от чистых функций. Чистая функция — это функция в математическом смысле, результат работы которой зависит только от входных данных. В ходе вычисления такой функции не происходит ничего лишнего: не меняются значения переменных, ничего не читается и не печатается, не пишется в БД и не выполняются запросы к внешним сервисам.&#x20;
4. Код станет проще отлаживать и тестировать. Этот пункт вытекает из двух предыдущих: у нас имеется набор небольших функций, часть из которых чистые, т.е. мы знаем, что их результат зависит только от входных данных. Код становится удобнее отлаживать — достаточно проверить, что возвращают используемые функции по отдельности, чтобы понять, как они будут работать вместе. Так же легко пишутся юнит-тесты для «чистой» логики вашего приложения.&#x20;

**Минусы:**

1\. На чистых функциональных языках не существует эффективного неупорядоченного словаря и множества.\
&#x20;С 90х годов использование словарей в разработке побило все рекорды. Словари теперь—стандартная коллекция, которую каждый разработчик ожидает найти в своей стандартной библиотеке. Чисто функциональные словари обычно примерно в 10 раз медленнее, чем хорошо реализованные хэш-таблицы, и я видел случаи, когда разница достигала 40 раз. Для некоторых приложений это непозволительно медленно.

2\. Не существует чисто функциональных потокобезопасных коллекций

По определению, неизменяемые коллекции не могут поддерживать мутабельность, в том числе и потокобезопасную. Так что, если вы хотите shared изменяемую коллекцию (такую как in-memory базу данных), то чисто функционального решения не существует.

3\. Большинство алгоритмов на графах выглядят хуже и работают намного медленнее, если написаны в функциональном стиле

&#x20;Функциональное программирование—отличный инструмент для некоторых типов задач, но, по моим наблюдениям, алгоритмы на графах—то место, где чисто функциональные решения часто хуже и с точки зрения простоты кода, и с точки зрения производительности.

4\. Инерция традиционных императивных структур данных и алгоритмов огромна\
Помимо алгоритмов на графах, существует много областей computer science, в которых 65 лет научной литературы фокусировались почти полностью на императивных решениях. Следовательно, программисты на императивных языках могут [стоять на плечах гигантов](https://en.wikipedia.org/wiki/Standing_on_the_shoulders_of_giants) и пользоваться этими наработками, в то время как программистам на функциональных языках часто приходиться начинать с нуля.&#x20;

5\. Как оказывается, все существующие реализации функциональных языков—как чистых, так и нечистых (impure)—спроектированы так, что аллоцируют слишком много памяти

6\. Чистое функциональное программирование хорошо для параллелизма в теории, но не очень хорошо с точки зрения производительности на практике, а высокая производительность на практике—единственная настоящая задача параллелизма\
Сегодня есть две причины писать параллельные программы. Первая—это писать объективно быстрые решения. Вторая—делать медленные решения не такими медленными. В большинстве случаев параллельное программирование на функциональном языке—вариация второй причины. Почти никто в среде высокопроизводительных вычислений (high performance computing), то есть на суперкомпьютерах, не запускает напрямую функциональный код. Если разработчик на функциональном языке параллелизует программу, в большинстве случаев он делает это не для того, чтоб добиться отличной абсолютной производительности, а чтоб улучшить ту скорость, что у него есть.

7\. О функциональном программировании циркулирует множество ложной информации\
Долгое время сообщество функционального программирования потрясало замечательно короткими реализациями алгоритмов решета Эратосфена и quicksort. Их даже годами преподавали студентам. И только спустя многие годы пришло понимание, что эти реализации не соответствуют исходным алгоритмам. Мелисса О'Нил (Melissa O’Neill) даже опубликовала [научную статью](http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf), исправляющую решето Эратосфена в Хаскеле. В частности, ее настоящее решето требует в сто раз больше кода, чем ошибочная оригинальная версия. То же справедливо и для quicksort'а, где «элегантная» двухстрочная версия на Хаскеле более чем в 1000 раз медленнее версии Сэджвика на C, потому что Хаскель делает глубокую копию списков на каждом вызове quicksort'а, портя к чертям асимптотическую сложность оригинального алгоритма Хоара.<br>
