Языки-сундуки и языки-чемоданчики. Часть 4
Oberon славится своей простотой и концептуальной сбалансированностью. Но его простота сложна. Сложна для тех, кто привык сложность перекладывать со своих плеч на чужие (с прагматики языка на его семантику). Язык C++ представляет собой пример иного подхода — попытки убрать сложность путём введения многочисленных инструментов. Но при этом вместо одной проблемы возникают другие: сложность переносится на иной уровень и становится нередко неконтролируемой — количество связей между базовыми сущностями и структурами (включёнными в семантику языка) начинает возрастать, они вступают в явные и неявные конфликты, распутывать которые должен будет тот, кто этот язык применяет.
Проф. Вирт в языке Oberon шёл вполне понятным путём. Сначала выбирается область применения инструмента (цели): системное программирование применительно к разработке операционной системы Oberon (однопользовательская ОС класса Developer's OS), в которой главными требованиями являются динамическая расширяемость систем (extensible programming) при изолированности (герметичности) системы типов. Затем используется принцип концептуальной экономии (по Энтони Хоару): проф. Вирт брал сбалансированный набор базовых сущностей и их отношений (связей, правил — моделью была классическая Modula-2) и удалял из языка (синтаксиса и семантики) те вещи, которые не разрушали исходные цели. Иными словами, искал ту грань, за которой система (в данном случае язык Oberon) будет оптимизирована по критерию концептуальной экономии и не рассыплется. Этой цели он добился. Для реализации системы Oberon (Oberon System) язык Oberon выглядит близким к идеалу.
Теперь посмотрим на C++. Цели, которые ставил перед собой Бьёрн Страуструп, существенно отличались от целей, поставленных Виртом перед языком Oberon. Страуструп хотел, с одной стороны, воплотить идеи своего любимого языка (Simula-67), оставаясь на базе синтаксиса (и семантики!) языка C. Т.е. обеспечить преемственность. И в этом плане с Виртом они не сильно расходятся (преемственность по синтаксису и семантике у Oberon с Modula-2 даже куда выше, чем у C и C++). А вот с другой — он хотел создать объёмный универсальный самодостаточный язык, который может практически всё. Поиск пресловутого философского камня.
Проблема Страуструпа была в другом — он хотел насытить язык многими полезными вещами. А каков в этом случае критерий развития языка? Почему можно прямо в язык (не в библиотеки) добавить одну полезную вещь и не добавлять другую? У Вирта удаление «лишней» полезной вещи приводит к рассыпанию системы. Нарушает ортогональность базиса. У Страуструпа добавление «лишней» вещи не рассматривается как разрушение системы, поскольку система (язык) воспринимается сама по себе в отрыве от того, кто ею оперирует (и кто хранит в своей голове полезные и вредные связи между плодящимися сущностями языка). В этом и есть главная проблема подобных языков, стремящихся вобрать в себя побольше всякого полезного. Нет сдерживающего фактора, кроме чувства меры (а больше вкусовщины) комитета по стандартизации (уже не самого Страуструпа, мнение которого здесь далеко не решающее). К чему ведёт такой подход? К неконтролируемому росту сложности как самого инструмента, так и сферы его практического применения.
Если кто-то пытается применять систему (язык Oberon) вне той задачи, для которой она предназначалась и оптимизировалась, то следует задуматься: виноват инструмент, его автор или же тот, кто, не понимая инструмента, пытается его применять где можно и где нельзя. Oberon — это язык-ядро, язык-чемоданчик. Это не язык-оболочка, не язык-сундук. Для использования в области промышленного производства программного обеспечения, промышленной разработки сложных программных систем, характеризующейся разделением труда при наличии большого числа программистов, классический Oberon требует соответствующей инфраструктуры (как, скажем, его диалект — Component Pascal и инструментальная система BlackBox), не отвергающей, а впитывающей в себя дополняющие языки (в их синтаксических одеждах). Только в этом случае он, пожалуй, будет адекватен решаемым задачам в этой сфере. В качестве иллюстрации к сказанному хотел бы напомнить слова великого немецкого философа Иммануила Канта, которые нам не стоит забывать: «Кто отказался от излишеств, тот избавился от лишений».
Языки-сундуки и языки-чемоданчики... Интересная метафора проф. В. Ш. Кауфмана (МГУ). Думаю, имеет смысл сделать ещё одно небольшое отступление и ввести читателя в контекст обсуждения. Очень рекомендую тем, кто серьёзно интересуется вопросами зодчества языков и их концепций, перечитать блестящий курс В. Ш. Кауфмана «Языки программирования. Концепции и принципы» (1993). Этот курс читался на факультете вычислительной математики и кибернетики МГУ. Проф. Кауфман пишет: «Безнадёжно строить языки программирования с моделями, заготовленным «на все случаи жизни». Однако можно попытаться построить язык программирования, на базе которого будет удобно (относительно несложно, с приемлемыми затратами) строить модели весьма разнообразных проблемных областей. Такой язык называют базовым языком программирования… Главное назначение базового языка — строить модели проблемных областей с тем, чтобы уменьшить сложность программирования в них. На примере Ады мы видели, как выявляемые технологические потребности приводили к новым конструктам. Может показаться, что на этом пути будут получаться всё более качественные языки программирования. К сожалению, большинство современных индустриальных языков программирования носят на себе родимые пятна такого примитивного критерия качества… Основной принцип конструирования, которым руководствовались авторы этих языков программирования, в упрощённой форме можно сформулировать так: для каждой значимой в проблемной области технологической потребности в языке должно быть готовое выразительное средство. Короче: каждой значимой потребности — новый конструкт. Этот принцип заготовленности конструктов и назовём принципом сундука (именно в сундуках хранят много всякого на всякий случай). Как показывает опыт, безудержное применение принципа сундука ведёт к громоздким, сложным, дорогим в реализации, обучении и использовании языкам-монстрам с тяжеловесным базисом и несбалансированными средствами развития… Никлаус Вирт неоднократно отмечал, что самое трудное при создании языка программирования — решить, от чего следует отказаться. Объясняя принципы конструирования своего (теперь уже предпоследнего) языка Modula-2 (поразившего специалистов элегантностью), Вирт развил эту идею и сформулировал следующий принцип языкового минимума: в язык программирования следует включать такие концепты и конструкты, без которых совершенно невозможно обойтись. Назовём этот принцип минимума принципом чемоданчика по контрасту с принципом сундука (в чемоданчик кладут только абсолютно необходимое)».
Проф. Вирт, на мой взгляд, верно нащупал стиль классицизма в развитии инструментария. В то время как все, увлекаясь архитектурой барокко, помчались насыщать языки всё новыми могучими средствами и переносить центр тяжести на графические среды, он понял, что идти надо в противоположном направлении. Вычленяя квинтэссенцию и вынося за рамки компетенции данного языка паразитную нагрузку. Это напоминает выделение в наборе команд процессора базового минимума, из которого строится остальное (RISC), напоминает идею микроядра ОС, напоминает Forth-системы… Микроядро — один из важных принципов создания программных и технических систем.
Oberon — это удачный вариант микроядерного языка традиционного императивного программирования, ориентированного на самую распространённую процессорную модель Эккерта-Неймана. Другим языком-микроядром можно назвать классический C. Отличительной особенностью Oberon является сбалансированность языка, опирающаяся на принцип концептуальной экономии и внутреннюю ортогональность: механизм расширения типов (type extension), обобщение процедур (процедурные типы), концепция модуля.
Вообще говоря, число языков-ядер (не обязательно микроядер) существенно меньше общего числа языков, но тем не менее языки-ядра представляют возможности выбора с учётом исповедуемого ими понятийного пространства. В дополнение к упомянутым Oberon и C можно отметить классику (а не современные вариации): Lisp, Рефал, Prolog, Forth, Smalltalk, Pascal, Occam.
Руслан Богатырев из цикла "Никлаус Вирт. Заветы смиренного зодчего".