Как известно, программисты — люди творческие, но вместе с тем ревностно придерживающиеся определенных идей, к примеру, выбора языка программирования. PHP считается языком «для ленивых», а JavaScript — «труднопрогнозируемой» магией. И среди огромного обилия языков функциональные языки все быстрее обрастают поклонниками и все увереннее прокладывают себе путь в большинство компаний по всему миру. Согласно аналитике RedMonkот июня 2017 и сборной оценке популярности языков на GitHub и Slack Overflow, функциональные языки (Elm, Elixir) медленно, но уверенно набирают рост. Огромный рост популярности JavaScript также ведет к повышенному интересу к ФП. Вдобавок, разработчики с опытом в функциональном программировании впоследствии начинали работать над SPA фреймворками, и, как результат, у нас есть Redux, React, MobX и другие библиотеки, которыми пользуются миллионы людей.
Так что же такое функциональное программирование, откуда такой бум и почему стоит задуматься о его изучении? Давайте разбираться.
Немного азов
Прежде чем уходить в дебри, стоит начать со старта.
Мир JavaScript кипит. Несколько лет назад лишь немногие разработчики имели представление о функциональном программировании, а вот за последние три года практически каждая кодовая база крупных приложений активно использует идеи, взятые из мира функционального программирования. И на то есть объективные причины: функциональное программирование позволяет писать более сжатый и предсказуемый код, его легче тестировать (хотя изучение с нуля дается нелегко).
Основные отличительные признаки разработки ПО при помощи ФП:
- компоновка чистых функций;
- избегание разделяемого состояния (shared state), изменяемых данных (mutable data) и побочных эффектов (side-effects);
- превалирование декларативного, нежели императивного подхода.
Функциональное программирование по своей сути основывается на фундаментальных, определяющих принципах, перечисленных выше. И чтобы начать их постигать, сперва стоит переключиться в «академический» режим и как следует изучить определения терминов, которые будут неотступно преследовать вас в ФП: чистые функции, композиция функций, избегание разделяемого состояния и т. п. Это больше похоже на возврат в школу на урок математики — зато после этого вы совершенно по-другому будете смотреть и на функциональное, и на программирование в целом.
Давайте немного углубимся в термины ФП и минимально поверхностно разберемся, что они значат.
Чистые функции — это детерминированные функции без побочных эффектов. Детерминированная функция означает, что для одного и того же набора входных значений она возвращает одинаковый результат. Для ФП свойства таких функций очень важны: к примеру, чистые функции обладают ссылочной прозрачностью — можно заменить функциональный вызов с его итоговым значением, не меняя при этом значения программы.
Композиция функцийозначает процесс комбинирования двух и более функций с целью создания новой функции или проведения вычислений.
Теперь сделаем паузу, выдохнем и вернемся к более понятным вещам, а именно, к разделяемым состояниям. Основная проблема разделяемых состояний — чтобы понимать эффекты функции, нужно знать всю историю каждой разделяемой переменной, которую использует функция. Поэтому функциональное программирование избегает разделяемых состояний, вместо этого полагаясь на неизменяемые структуры данных и чистые вычисления с целью извлечения новых данных из существующих. Еще один нюанс, который возникает при работе с разделяемыми состояниями — изменение порядка вызова функций может повлечь за собой лавину ошибок. Соответственно, избегая разделяемых состояний, вы также избегаете этой проблемы.
В основе всего функционального программирования лежит неизменность (immutability). И тут важно не путать const
с неизменностью. const
создает связывание имени переменной, которое не может быть переприсвоено после создания, но оно не создает неизменяемые объекты. Вы не сможете изменить объект, к которому относится связывание, но вы все еще сможете менять свойства этого объекта, соответственно, связывания, созданные const
, не являются неизменяемыми. Неизменяемые объекты вообще не могут быть изменены. Это достигается глубокой заморозкой переменных.
И, если вы еще не совсем устали, на секунду рассмотрим побочные эффекты. Побочные эффекты означают, что помимо возврата значения функция также взаимодействует с внешним изменяемым состоянием. Почему ФП их избегает? Потому что таким образом эффекты программы гораздо легче понимать и тестировать. Haskell, к примеру, для изоляции побочных эффектов от чистых функций, использует монады.
Итак, ваша голова, скорее всего, начала побаливать, но обязательно найдется кто-то, кто спросит: так все-таки, почему декларативный, а не императивный подход? В чем соль?
Соль в том, что императивный подход работает по принципу управления потоком и отвечает на вопрос «как делать». Декларативный же подход описывает поток данных и отвечает на вопрос «что делать». Вдобавок, императивный код чаще всего использует инструкции (операторов), а декларативный больше полагается на выражения.
Итак, мы вроде бы разобрались с тем, что такое функциональное программирование и что нужно о нем знать. И, прежде чем мы перейдем к обсуждению его преимуществ, предлагаю сперва пройтись по недостаткам — точнее, вникнуть в суть стереотипа «функциональное программирование неестественно».
Чтобы написать код, станьте кодом
Судя по тому, что я писала выше, среди читателей уже должны были зародиться адепты функционального программирования. Тем не менее, несмотря на огромное количество хвалебных статей, находится не меньшее количество статей под названиями «Функциональное программирование странное и не имеет будущего» (к примеру). Означает ли это, что я заблуждаюсь? Нет. Есть ли причины считать ФП странным? Разумеется.
Позвольте приведу цитату, взятую с просторов Интернета, которая полностью отражает отношение многих разработчиков к ФП:
«Написание функционального кода — словно написание задом наперед и чаще всего похоже на разгадывание головоломки, а не на объяснение процесса компьютеру».
На самом деле, это довольно субъективное отношение к ФП тех, кто не хочет уделить достаточное количество времени тому, чтобы разобраться в нюансах функционального программирования и просто попробовать. Но, как я и обещала, разберем недостатки ФП, чтобы потом перейти к преимуществам.
Недостатки
Во-первых, для функциональных языков нет эффективного неупорядоченного словаря и множества. Чисто функциональные словари работают медленнее хэш-таблицы, и для некоторых приложений это может быть критично. Во-вторых, не существует чисто функциональных слабых хэш-таблиц, хотя для большинства разработчиков этот недостаток может остаться незамеченным.
Также ФП не подходит для алгоритмов на графах (за счет медленной работы) и в целом для тех решений, которые десятилетиями основывались на императивном программировании.
Ладно, последний пункт больше звучит как придирка — мы не можем винить ФП в том, для чего оно не было предназначено. Поэтому предлагаю обратиться к приятному, а именно — к преимуществам ФП и его использованию в реальных проектах реальными международными компаниями.
Преимущества
Один из самых явных плюсов функционального программирование — это высокоуровневые абстракции, которые скрывают большое количество подробностей таких рутинных операций, как, например, итерирование. За счет этого код получается короче, и, как следствие, гарантирует меньшее количество ошибок, которые могут быть допущены.
Далее, в ФП содержится меньшее количество языковых примитивов. Хорошо знакомые всем классы в ФП просто-напросто не используются: вместо создания уникального описания объекта с операциями в виде методов, в функциональном программировании используется несколько основных языковых примитивов, хорошо оптимизированных внутри.
Вдобавок, функциональное программирование позволяет разработчику приблизить язык к проблеме, а не наоборот, и все за счет гибких структур и пластичности языка. К тому же, ФП предлагает разработчикам новые инструменты для решения сложных задач, которыми ООП программисты зачастую пренебрегают.
На самом деле, перечислять все преимущества функционального программирования слишком долго — их правда много. Могу сказать так: работа с функциональными языками обеспечивает точное и быстрое написание кода, облегчает тестирование и отладку, программы более высокоуровневые, а сигнатуры функций более информативны.
Разумеется, нельзя отрицать преимущества ООП, но стоит помнить и о том, что функциональные языки по своему удобству стоят наравне со многими другими и достойны вашего внимания.
Гребень волны IT-трендов и применение ФП
В мире IT ничего не происходит просто так. Одно цепляется за другое, и вот уже все самые «горячие» тренды связаны между собой.
Если вспоминать наиболее нашумевшие тренды
В настоящее время очень остро стоит проблема параллельной обработки и работы с большими потоками данных, другими словами, работа с Big Data. И, распараллелив обработку этих данных, можно получить желаемый результат за долю секунды, что очень критично в реальном мире. Плюс не забывайте о децентрализованных (распределенных) вычислениях — блокчейнах и других, которые, по сути своей, являются довольно сложным механизмом. И для таких вычислений функциональный код подходит больше всего за счет всех принципов функционального программирования (таких, как чистые функции, например). Использование всех базовых приемов ФП облегчает параллельное выполнение кода и его поддержку.
Вдобавок, если раньше функциональное программирование использовалось только для решения специфических задач, то теперь оно применяется даже на классических проектах. Из чего можно смело делать вывод: крупным IT — компаниям не стоит сомневаться по поводу использования функционального программирования, а в качестве примера можно привести один из проектов Intetics, на котором занимаются разработкой продвинутых Front-end приложений с интерактивным UI.
Суть проекта
Еще до того, как команда Intetics присоединилась к проекту, заказчик разработал собственный функциональный язык программирования. О причинах мы поговорим чуть позже, а пока что ответим на вопрос: почему именно функциональное программирование?
Суть проекта заключается в разработке системы адаптивного обучения для широкой аудитории: от образовательных учреждений до различных неакадемических курсов и предметов, как виноделие, теория игры в гольф и т. п. Еще один крупный сегмент проекта — корпоративное обучение, которое подразумевает разработку обучающего курса для специфичных нужд конкретной компании или организации.
Заказчик был не только инноватором (на тот момент это было первое подобное решение на рынке), но и весьма проницательным человеком. Он разглядел все достоинства функционального программирования раньше своих конкурентов, и это позволило ему обеспечивать высокое качество своего продукта и поддерживать его в соответствии с требованиями. То есть ФП обеспечивало лаконичность кода, плюс, подходило клиенту по стилю. Справедливости ради стоит сказать: ООП тоже подходит под решение задач проекта, и заказчик пробовал несколько языков, но выбор все же остановил на функциональном программировании.
Говоря о причинах разработки собственного языка, не забывайте, что мы говорим о проекте десятилетней давности. В то время не было достаточно развитых кроссплатформенных фреймворков, подходящих для данных задач, и поэтому заказчик просто взял и создал собственный — flow для быстрого создания UI под разные платформы. И это очень облегчает работу, позволяя быстро внедрять любые изменения и решать любые задачи, связанные с проектом. К тому же, кроссплатформенных функциональных языков очень мало, так что проект действительно выделяется среди своих аналогов.
Вывод — не бойтесь пробовать
Как вы уже наверняка поняли, функционального программирования опасаться не стоит. Немного усердия и любознательности, и вот вы уже освоили ФП. Вот несколько советов тем, кто решился попробовать себя в новом жанре и выучить что-то кардинально новое:
- Расслабьтесь :) Поначалу и правда будет сложновато, учитывая, что вам придется отодвинуть на задний план то, что вы знаете, и учить новые подходы и принципы. И это нормально. Вспомните, как вы впервые учились кодить. Ничего страшного не случится.
- Начинайте с микрозадач, чтобы «набить руку».
- Начните изучать Haskell и затем переходите на Scala (или F#), чтобы начать вникать в принципы функционального программирования (а заодно начать мыслить более «функционально»).
- Вам могут пригодиться эти ресурсы:
- Книга «Learn You a Haskell»
- Книга «Real-World Functional Programming: With Examples in F# and C#»
- Курс «Programming Languages, Part A» (для изучения концептов ФП с помощью SML, Racket, Ruby)
- Курс «Introduction to Functional Programming»
- Подборка на GitHub (JavaScript)
- И еще однаподборка GitHub «Awesome FP JS»