"Грамотное программирование"

21 май, 2002 - 23:00Андрей Зубинский
Предмету сегодняшнего нашего обзора, пожалуй, можно присвоить титул "Самый необласканный вниманием массовой компьютерной прессы". С одной стороны, это можно понять -- речь идет о весьма специфической и далекой от стандартизации "почти методологии". С другой -- изящная в своей простоте идея, ставшая основой обширного перечня инструментальных средств, подтвержденная успешностью практического применения, заслуживает чего-то большего.

Literate Programming

Давайте изменим традиционные приоритеты в создании программ: вместо представления о нашей задаче как о создании инструкций "Что делать?" для компьютера сконцентрируемся на объяснении другим людям описаний нашего видения того, что под управлением программы должен делать компьютер.
Дональд Кнут

Термин "грамотное программирование" (Literate Programming -- LP) был изобретен Д. Кнутом -- автором алгоритмических основ "компьютерного типографского набора", знаменитой программной системы TeX и, наконец, единственного фундаментального энциклопедического труда в области компьютинга -- "Искусство программирования". Звучание дословного перевода сразу уместно будет усилить (или даже уточнить) вариантом, основанным на созвучии слов literate и "литература" -- "литературное программирование" (ЛП). Как ни странно, но уточнение, построенное на таком весьма опасном допущении (аналогии всегда опасны), окажется намного удачнее, чем точный дословный перевод.

"Грамотное программирование"
Текстовый редактор Emacs -- редкий представитель инструментальных средств, интерактивно поддерживающих методологию ЛП

Выбранная в качестве эпиграфа цитата, датируемая далеким 1984 г. (именно тогда увидела свет первая официально зарегистрированная публикация, посвященная ЛП, -- статья Д. Кнута "Literate programming"), дает точное и краткое описание главного в любой методологии -- цели. И здесь следует сделать первое важнейшее отступление, а именно, попытаться воссоздать характер программирования как массового явления тех времен. Без уточнения этой необходимой детали слишком многое из сказанного впоследствии потребует дополнительных сложных и спорных пояснений.

В конце 70-х -- начале 80-х годов прошлого столетия программирование уже выросло из "коротких штанишек", а производительность компьютеров достигла уровня, допускающего решение сложных наукоемких задач. Но подобные компьютеры (и компьютеризация как массовое явление) все еще оставались атрибутом, если так можно выразиться, "серьезных организаций" -- оборонных, крупных научных и исследовательских, больших корпораций и банков. Высокая стоимость аппаратных средств в те времена, как ни странно, играла и положительную роль -- после бурного периода развития с его неизбежными ошибками, провалами и достижениями уже было понятно, что программы стоят дороже, чем компьютеры, поэтому высокие бюджеты программных проектов никого не пугали. Возможно, именно по этой причине многие из созданных в те времена прикладных программных систем стали "долгожителями": несмотря на убогость пользовательских интерфейсов, в них заложена мощнейшая алгоритмика, за которой стоят иногда тысячи человеко-лет кропотливого процесса разработки. В целом, модель "дорогие компьютеры -- хорошая производительность -- большой бюджет процесса разработки ПО -- высокая алгоритмическая сложность" хорошо характеризует этот период.

Идея ЛП, как детище своего времени, очень точно соответствует нашей примитивной модели -- и интерес к этой идее, и развитие инструментальных средств и самой идеи постепенно угасали с популяризацией и персонализацией компьютеров. Этот очевидный факт можно пояснить, не выходя за рамки принятой модели -- повышение доступности компьютеров означает и глобальное смещение приоритетов в алгоритмической основе ПО. Естественно, алгоритмически сложное ПО никуда не исчезает, но на общем фоне его роль уже далеко не столь важна, как в период создания Д. Кнутом идей ЛП. Сегодня правят бал пользовательские интерфейсы, а сложная алгоритмика в панике упрятывается в повторно используемые компоненты -- в панике из-за того, что массовый рынок требует слишком много программ "сразу и сейчас", а для разработки такого количества ПО просто невозможно найти необходимое количество программистов класса Д. Кнута.

И все-таки области, где применение идей ЛП не теряет своей актуальности, никуда не исчезают -- управляющие программы для встраиваемых систем, высоконадежные программные комплексы класса "mission critical", критическое системное ПО и, наконец, -- столь модное сегодня "открытое ПО". О последнем стоит сказать особо -- именно идеи "открытого ПО" (распространяемого на уровне исходных текстов), казалось бы, идеально согласуются с целью ЛП. Ведь доступность исходных текстов -- не самоцель различных сообществ и не прихоть авторов той или иной лицензии, напротив, это критический фактор жизнеспособности программ. Казалось бы, что именно здесь идее ЛП суждено было обосноваться прочно и надолго, и "открытое ПО" необходимо создавать именно на основе принципа "объясняй человеку, как должен действовать компьютер". На деле же... фраза "я слишком ленив, чтобы писать документацию" (I'm too lazy...) -- чуть ли не неизменный атрибут массы свободно распространяемых программ.


Детализация

Причина, из-за которой мое объяснение слишком длинное, -- отсутствие времени для того, чтобы сделать его короче.
Блез Паскаль

Цель ЛП прозрачна и даже благородна. Ее можно сформулировать и другими словами, а именно: "главное в работоспособной программе -- не управляющие инструкции компьютеру, а знания разработчика программы, выраженные в максимально удобной для понимания сторонним человеком форме". В реальности большинство программистов... сталкивались с образчиками ЛП при изучении посвященных алгоритмическим основам своей профессии книг, авторы которых практически всегда прибегают к приему описания реализаций программ на псевдокоде -- гибриде языка программирования и естественного языка. Впрочем, крохотный пример может показаться не лишним, а заодно и дать первое ощущение "привкуса" ЛП:


Стиль C и C++

for( ic=0, ic<MaxCusts, ic++ )
if ( Cust[ic].age > 18 )
...


Стиль ЛП

@<Для каждого из зарегистрированных потребителей, достигшего совершеннолетия>@


Давайте сразу обратим внимание на пары символов '@<' и '>@'. Они своей симметрией наводят на аналогию с... символами-ограничителями тегов в языках разметки, например HTML. И, как ни странно, во второй раз аналогия не подвела -- все воплощения идеи ЛП являются специализированными языками разметки, поддержанными инструментально.

Естественно, у читателя должен возникнуть вопрос -- "Ну и что это дает?". Действительно, зачем вводить еще один язык, если можно (и нужно) просто комментировать текст программы? Чтобы ответить на подобные вопросы, достаточно узнать самое главное о соотношении синтаксиса и семантики языков ЛП.

Синтаксически языки ЛП крайне просты и представляют собой небольшой перечень правил записи объединения высокоуровневых фрагментов псевдокода, допускающего описание программы на естественном языке, и фрагментов-"реализаций" этого псевдокода, в которых последний "реализуется" на некотором языке программирования. Принципиальное свойство языка ЛП -- произвольный (выбираемый программистом) порядок следования в ЛП-программе фрагментов-"реализаций".

Семантика языка ЛП, несмотря на простоту синтаксиса, куда более сложна. Фактически язык ЛП формирует принципиально новую парадигму программирования, не зависящую (или минимально зависящую) от языка реализации программы. К особенностям этой "ЛП-парадигмы" разные исследователи и программисты-практики относят "плавность" перехода от описания проблемы (постановки задачи) к реализации (программе), полноценную поддержку разных методик анализа и разработки (например, гибридной: нисходящий анализ и восходящая реализация), эффективное воплощение принципа "разделяй и властвуй" и т. д. Сторонники наиболее радикальной точки зрения на ЛП считают, что такие особенности позволяют говорить о ЛП-языках как о принципиально новых языках программирования, поддерживающих стиль программирования, при котором "...дисциплинированно делается именно то, что нужно делать".

Ну а теперь можно и ответить на поставленный вопрос. Вспомним принципиальную синтаксическую особенность языков ЛП -- произвольность порядка и отсутствие упоминаний о каких-либо ограничениях на содержание фрагментов-"реализаций". Казалось бы, незначительный нюанс, но в ЛП, как и в жизни, все строится на них. Вследствие таких особенностей об уровне применимости ЛП-подхода можно и не говорить -- он "работает" от самого высокого уровня (проектирования) до самого низкого, превращая и кодирование в процесс высокоуровневой разработки.


Пора объяснений

То, что написано без усилий, читается без удовольствия.
Сэмюэль Джонсон

Во всех материалах, посвященных ЛП, очень трудно отделить концептуальную часть от реалий применений. Впрочем, этим грешат все описания методологий, концепций и парадигм программирования -- чтобы по-настоящему "почувствовать вкус", требуется реальный пример, а не потешная программка класса "Hello, world", ведь материал адресуется тем, для кого "потешный" уровень программирования -- давно пройденный этап. Журнальная статья тем более не позволяет приводить "реальные примеры". Мы ограничимся только обсуждением нюансов.

Псевдокод ЛП-программ фактически представляет собой описание структуры будущего программного "шедевра". Абсолютно неформальное, допускающее включение математических формул, иллюстраций, чего угодно -- вплоть до мультимедийных "вставок", такое описание-документация "по ходу дела" дополняется новыми фрагментами псевдокода и фрагментами-"реализациями". При этом ЛП-программист не должен следить за сложной в больших программах "адресацией" -- соответствием команд псевдокода и фрагментов-"реализаций", так как сами фразы на псевдокоде являются "адресом". То есть, если где-то в ЛП-программе встретилась фраза псевдокода "@< Вывод сообщения на печать >@", впоследствии программист может "овеществить" ее следующим фрагментом-"реализацией":

@< Вывод сообщения об ошибке на печать >@=
prints("Error!\n);

Располагаться это "овеществление" может в тексте ЛП-программы где угодно, и если впоследствии реализующий псевдокод фрагмент кода по каким-то причинам надо изменить, достаточно произвести такое изменение только в фрагменте-"реализации".

Задача инструментов, поддерживающих ЛП-программирование, -- преобразование программы на ЛП-языке в текст, пригодный к трансляции в исполняемый код, и одновременное формирование качественной документации в том или ином представлении. Задача не слишком сложна, но и здесь есть свои нюансы -- произвольное расположение фрагментов-"реализаций" в тексте ЛП-программы приводит к необходимости "хитрого" механизма нумерации строк кода.


Развитие

Как и всякая методология, ЛП обладает массой специфических недостатков. Их можно характеризовать "часто высказываемыми возмущениями" (ЧВВ) в тематических сетевых конференциях: реализации слишком сложны, реализации недостаточно функциональны, с имеющимися реализациями трудно отлаживать программы и организовать коллективную разработку, реализации не поддерживают используемый нами язык программирования. К счастью, в подавляющем большинстве случаев речь идет о существующих реализациях инструментария ЛП, а не о самой идее ЛП -- последняя, напротив, даже при очевидном несовершенстве инструментов демонстрирует убедительные достоинства.

Казалось бы, что все предпосылки для развития ЛП-инструментария есть: во-первых, ЛП-методология работает, что доказано ее успешным применением не в одном реальном проекте, во-вторых, рост популярности открытого ПО остро ставит проблему качества документации, в-третьих, инструментарий ЛП весьма прост. И все же, никаких существенных событий с момента создания Д. Кнутом первой реализации ЛП-системы под названием WEB в мире "грамотного программирования" не произошло. Авторы новых инструментальных средств или увеличивают сложность ЛП-языка до невообразимых пределов, отпугивая тем самым многих программистов, прекрасно знающих, что значит изучение "еще одного языка", или сокращают функциональность до весьма сомнительного уровня.

Поразительно и то, что во всем изобилии материалов, посвященных ЛП (а их действительно много), до сих пор не была усмотрена напрашивающаяся аналогия между сочетанием "фрагмент псевдоязыка -- фрагмент-"реализация" и... структурами, используемыми для описания знаний, в первую очередь, с фреймами. Уже упоминавшийся ранее в весьма странном контексте термин "адрес" в данном случае получает ясное толкование: объекты ЛП-программы -- фрагменты псевдоязыка -- могут адресовать другие объекты и при этом включать дополнительные описания фрагментов реальных программ. Идеально соответствующая фреймовому представлению знаний картина, позволяющая говорить об ЛП-программе как о высокоуровневой базе знаний. Качество содержимого этой базы, естественно, определяется качеством работы создателей программы, и в этом плане ЛП как методология не способна привести ни к каким принципиальным улучшениям (опять же, как и любая методология вообще). А вот сама фреймовая модель открывает весьма неожиданные и привлекательные перспективы, связанные, в первую очередь, с коллективной разработкой программ и повторным использованием кода. Существующие отработанные реализации распределенных фреймовых баз знаний (например, система FramerD), по идее, позволяют создавать громадные репозитории ЛП-программ, находить подходящие для повторного использования фрагменты ЛП-кода, организовывать управление и совместную работу тысяч программистов над одним проектом.

Не менее поразителен и факт крайне слабого развития интерактивных инструментальных средств, поддерживающих ЛП. Наибольшие достижения в этой области наблюдались лет 10 назад, на платформе Macintosh, да и то пик их популярности, похоже, прошел.

Развития функциональности ЛП, скорее всего, не было вообще -- после Д. Кнута то ли никто не осмелился что-либо улучшить в идеях мастера, то ли эти идеи не столь сильно овладели умами... Хотя целый ряд усовершенствований напрашивается сам собой -- сочетание ЛП-систем и систем статической проверки программ, генерация высокоуровневых спецификаций на основе ЛП-кода, построение гибридных систем класса "ЛП -- формальный язык описания, пригодный к верификации".


Будущее?

Вопрос "Есть ли будущее у ЛП?" сегодня уже весьма актуален. Даже в хорошо подходящих для нее областях применения эта методология не нашла массового распространения -- в первую очередь, из-за несовершенства технологических средств. У ЛП есть фанатичные приверженцы, но, судя по крайне малому количеству реально созданных на основе ЛП программ, приверженность -- не главный решающий фактор в развитии. Как, впрочем, и фанатизм.