Всяческих концепций, идей и парадигм в программировании много. И это, в общем-то, хорошо, потому что каждый может выбрать ту, которая наилучшим образом сочетается с его внутренним видением программирования и с решаемой в данном случае задачей. Сейчас я познакомлю вас с одним из самых интересных, на мой взгляд, подходов к написанию программ, который наверняка будет особенно интересен тем из читателей, кто пока с ним не сталкивался.
"Подобно ООП, функциональное
программирование - это множество
идей, но не множество жёстких
правил"
Вячеслав Ахмечет
Чем плохи большинство статей по функциональному программированию, так это тем, что они весьма далеки от проблем простого программиста, которого повышение собственной зарплаты (к счастью) волнует гораздо больше, чем различные выкладки из области дискретной математики, которыми больны такие статьи. Поэтому я постараюсь не наступать на эти распространённые грабли - думаю, это получится, потому что я не математик.
Итак, какая же основная мысль лежит в основе парадигмы функционального программирования? Давайте повнимательнее присмотримся к самому названию. Не нужно быть филологом, чтобы заметить, что слово "функциональное" явно связано со словом "функция". В функциональном программировании функции понимаются в своём изначальном, математическом смысле - это некоторый набор операций, принимающих входное и возвращающих определённое значение каких-либо переменных. Чем это, спросите вы, отличается от обычного программирования, не суть важно - процедурного или объектно-ориентированного? Вопрос резонный, ибо в том изложении, в котором представил основную идею функционального программирования я, действительно сложно заметить какое-либо его отличие от традиционных концепций программирования.
Основное отличие состоит в том, что функции, как и функции математические, не производят никаких побочных действий. Они только принимают входные значения, крутят и вертят их так, как им заблагорассудится, а после выдают на-гора результат, радующий или не радующий программиста. При этом они не изменяют никаких флагов состояния, не записывают никакой информации в глобальные переменные, не пытаются освободить занятую другими объектами память. Видите, какое сразу вырисовывается преимущество: если программа состоит исключительно из функций, работающих только с переданными им переменными, то никакая функция не сможет вызвать ошибок доступа к памяти - а ведь именно такие ошибки наиболее противные в плане локализации их источников. Кроме того, становится не столь важным соблюдение последовательности выполнения операторов в программе - каждая функция может вычислить своё значение в любой момент, что позволяет легко распараллеливать программы, написанные в функциональном стиле.
Но, пожалуй, самое важное преимущество программ, написанных в функциональном стиле - это, конечно же, их логическая связность и краткость. Программы на функциональных языках программирования могут быть в десятки раз короче тех же самых программ, написанных на традиционных языках. Когда я говорю о традиционных языках здесь, то подразумеваю под ними императивные языки программирования - то есть такие, в которых для выполнения некой операции нужно пошагово описать её выполнение. Примеры таких языков - знакомые всем Basic, Pascal, C.
"В сравнении с пользователями С,
"никто" - достаточно точная
оценка числа пользователей
функциональных языков"
Филип Вадлер
Сразу после рассказа о бочке мёда я расскажу о ложке дёгтя. Действительно, как же так получается, что функциональным программированием, которое решает огромное количество наболевших программистских проблем, пользуются, мягко скажем, не все программисты? Тому есть множество причин. Часть из них я почерпнул из замечательной статьи Филипа Вадлера "Почему никто не использует функциональные языки?" (её можно найти по адресу www.softcraft.ru/paradigm/fp/whynotfp.shtml). Главными недостатками функциональных языков программирования он считает плохую совместимость с самыми популярными из императивных языков программирования, на которых сейчас написано подавляющее большинство широко используемого программного обеспечения, плохую переносимость программ на функциональных языках на различные платформы и низкую популярность этих языков (то есть, получается рекурсия - языки не популярны из-за того, что все опасаются серьёзно их использовать из-за их низкой популярности). Есть ещё ряд причин, но все они также, в большинстве своём, упираются в инерционность индустрии программного обеспечения, в том числе и в инерционность людей, которые работают в ней.
Тем не менее, тенденции теперь таковы, что функциональному программированию прочат блестящее будущее и популярность не меньшую, чем у программирования объектно-ориентированного. Всё-таки винторогих баранов, кричащих "Нам нужен только Си/Java/Ассеблер" (нужное подчеркнуть), среди разработчиков софта всё же меньше, чем здравомыслящих людей. А потому языки функционального программирования постепенно приживаются в тех нишах, для которых подходят наилучшим образом - и это вполне естественно, так как язык программирования, как и любой другой нишевый продукт, не может при всех своих реальных или мнимых достоинствах сразу захватить значительную часть рынка. Поэтому будьте терпеливыми: изучение функционального программирования - это такое вложение времени, которое не отразится сразу на вашей зарплате, зато позволит в будущем оставаться востребованным и высокооплачиваемым специалистом.
"Функциональный программист
смотрится как средневековый монах,
отвергающий удовольствия жизни в
надежде, что это сделает его
добродетельным. Для тех, кто
заинтересован в материальных
выгодах, эти "преимущества" не
очень убедительны. (...) Ясно, что
такая характеристика
функционального программирования
неадекватна."
Джон Хьюз
Самое вкусное я постарался оставить на потом. Вы обращали внимание, что на сборных концертах в финале всегда выступают самые именитые звёзды, а на сольных исполнители поют именно проверенные временем хиты, а не самые новые песни? Вот и в этой статье самые привлекательные особенности функционального программирования откроются самым стойким из читателей - тем, кто не бросил читать, а дочитал досюда.
Итак, первая "вкусность". В функциональном программировании это называется функциями высших порядков, в математическом анализе (или, если более точно, то в вариационном исчислении) - функционалами. Это функции, аргументами для которых могут быть другие функции. Не значения, возвращаемые этими функциями, - нет-нет, именно сами функции. Простейший функционал - это производная, другой пример - первообразная. Фактически, функции высших порядков - это некоторый аналог того, как объектно-ориентированные языки программирования позволяют обращаться программисту с классами. Функции высшего порядка позволяют очень просто масштабировать приложения. Например, при загрузке данных разного формата вы можете передавать соответствующую функцию загрузки базовой функции, которая сама вызовет нужную в зависимости от вида данных. Если придётся добавить ещё один формат, вы просто добавите ещё одну функцию, не изменяя при этом кода, отвечающего за логику приложения.
Теперь ещё одна возможность, которая, вероятно, покажется вам даже более интересной, так как её аналоги напрочь отсутствуют в императивных языках. Это так называемые ленивые вычисления, называемые также отложенными. Ленится при этом, правда, не программист (программисты и так существа невероятно ленивые в своём подавляющем большинстве), а сама программа. Что это значит? Это значит, что она откладывает все вычисления до тех пор, пока не понадобится их результат. Программист только описывает зависимости функций друг от друга (не забывая при этом, конечно же, использовать такую замечательную вещь, как функции высших порядков), а программа уже сама будет решать, когда вычислять значение той или иной функции. Таким образом, увеличивается производительность программы и, опять-таки, улучшается потенциальная возможность её грамотно распараллелить. Впрочем, ленивые вычисления поддерживаются не всеми функциональными языками (но самый популярный из них, Haskell, их поддерживает) и при этом поддерживаются многими языками программирования, не относящимися к функциональным (например, Python - но там они организованы несколько иначе).
Ещё одна любопытная деталь языков функционального программирования - это так называемый карринг. Что это такое? Оказывается, несмотря на несколько аляповатое и устрашающее непосвящённых название, всё очень и очень просто. Это способность функциональных языков программирования быстро создавать функции, являющиеся обёртками для других функций, не заставляя при этом программиста писать рутинный вспомогательный код. В объектно-ориентированных и процедурных (сиречь императивных) языках программирования для того, чтобы определить обёртку для какой-либо функции или метода, программист должен вручную прописывать все определения новой функции. Функциональные языки (не берусь, впрочем, утверждать, что все - их много, очень много) позволяют гораздо быстрее определить новую функцию, если нужно уменьшить число параметров оборачиваемой и заменить часть из них на заранее предопределённые константы.
В общем-то, этими тремя пунктами интересные и мощные особенности функциональных языков программирования далеко не кончаются - тема эта благодатная, и говорить о ней можно куда дольше, чем мне позволит главный редактор. Поэтому, думаю, пора сворачиваться и подводить итоги нашего с вами разговора о функциональном программировании.
Во-первых, функциональное программирование - мощная парадигма, позволяющая устранить многие минусы объектно-ориентированного программирования (ну и при этом наломать новых дров - куда же без этого?). Хотя сейчас парадигма эта и не очень популярна в индустрии программного обеспечения, тем не менее, у неё есть все шансы стать куда более распространённой, чем сейчас. Во-вторых, функциональное программирование уже сегодня предстаёт в виде множества специально разработанных под эту парадигму языков, которые позволяют программисту делать многие такие вещи, которые вовсе недоступны в императивных языках. Ну, а в-третьих, подходы, которые предлагает функциональная парадигма, можно вполне успешно применять и в нефункциональных языках, делая благодаря им свой программный код лучше. Так что функциональное программирование, как видите, заслуживает того, чтобы им интересоваться и его изучать.
Вадим СТАНКЕВИЧ
Горячие темы