Джон Остераут
Компания Scriptics,
ouster@scriptics.com

Языки программирования систем
Языки высокого уровня
Типизация
Языки сценариев
Бестиповость
Интерпретируемость
Разные средства для разных задач
Рост значимости языков сценариев
Программисты по случаю
Роль объектов
Другие языки
Заключение
Литература
Выбор типа языка

В условиях постоянного роста производительности компьютеров и изменений в масштабах и номенклатуре приложений все большую значимость приобретает такой инструмент, как языки сценариев (scripting languages). Одно из фундаментальных изменений в подходе к программированию за последние 15 лет связано с переходом от традиционных языков программирования систем, таких как Си или С++, к языкам описания сценариев наподобие Perl или Tcl. На практике многие специалисты так или иначе вовлечены в этот процесс, но далеко не все осознают, что на самом деле происходит. В данной статье мы попытаемся объяснить, почему в следующем столетии языки сценариев лучше подойдут для решения многих задач, чем языки программирования систем.

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

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

Языки программирования систем

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

В конце 50-х начали появляться языки более высокого уровня: Лисп, Фортран и Алгол, в которых уже не было однозначного соответствия между операторами и машинными командами; компилятор транслирует каждый оператор исходной программы в последовательность двоичных команд. В дальнейшем линия Алгола продолжилась в целом ряде языков программирования систем: PL/1, Паскаль, Си, С++ и Java. Постепенно они почти полностью вытеснили языки ассемблера при разработке больших приложений.

Языки высокого уровня

Выражение "более высокий уровень" применительно к языку означает, что многие детали обрабатываются в нем автоматически, поэтому программистам при решении той же проблемы приходится писать меньше кода. Например:

  • назначение регистров выполняется компилятором, и программистам не требуется писать код для пересылки информации между регистрами и памятью;
  • последовательности вызова процедур генерируются автоматически, а программисты могут не беспокоиться о пересылке аргументов в стек вызова и из него;
  • для управляющих структур можно использовать простые ключевые слова, такие как while и if - компилятор сам генерирует все необходимые для их реализации машинные команды.

В среднем, каждая строка кода в языках программирования систем транслируется примерно в пять машинных команд, сравнительно с одной командой на строку в ассемблерной программе. В неформальном анализе восьми файлов на Си, написанных разными людьми, я обнаружил, что это отношение простирается от трех до семи команд на строку кода [1]; в своем известном исследовании Кейперс Джоунс (Capers Jones) показал, что для заданной задачи по сравнению с языками программирования систем языки ассемблера требуют написания большего количества строк кода в отношении от трех до шести раз [2]. Программисты могут писать примерно одно и то же количество строк кода за год независимо от языка [3], поэтому языки программирования систем позволяют создавать приложения быстрее, чем языки ассемблера.

Типизация

Еще одно коренное отличие языков ассемблера от языков программирования систем - в типизации. Я использую термин типизация (typing) для обозначения той степени, с которой смысл информации специфицируется заранее - до ее использования. В сильно типизированном языке программист декларирует, как каждая порция информации будет использована, а язык предотвращает ее использование другим способом. В слабо типизированном языке не существует априори заданных ограничений на использование информации; ее смысл определяется только способом, которым она используется.

Сами по себе современные компьютеры принципиально бестиповые. Любое слово памяти может хранить величину любого типа, будь то целое или вещественное число, указатель или команда. Смысл (значение) величины определяется тем, как она применяется. Если счетчик команд указывает на слово памяти, то оно трактуется как команда; если на слово ссылается команда сложения целых, то оно трактуется как целое, и т.д. Одно и то же слово может использоваться в разных случаях по разному.

Современные языки программирования систем, напротив, сильно типизированы, например:

  • каждая переменная должна быть изначально декларирована с тем, чтобы быть приписанной к определенному типу (целое, указатель на строку и т. д.), и должна использоваться, теми способами, которые этому типу соответствуют;
  • данные и код разделены - трудно, если вообще возможно, создать новый код во время выполнения;
  • переменные могут быть сгруппированы в объекты с хорошо определенной структурой и процедурами для манипуляции ими. Объект одного типа не может быть использован там, где ожидается использование объекта другого типа.

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

Языки сценариев

Языки Perl [4], Python [5], Rexx [6], Tcl [7], Visual Basic и оболочки Unix (shell) представляют стиль программирования, отличный от присущего языкам программирования систем. Этот стиль предполагает, что набор полезных в контексте решаемой задачи компонентов уже существует и остается должным образом их скомбинировать. Например, Tcl и Visual Basic могут служить для размещения на экране набора управляющих элементов пользовательского интерфейса, а написанные на shell командные файлы позволяют собрать программы-фильтры в конвейеры. Хотя языки сценариев часто используются для расширения свойств компонентов, они мало пригодны для программирования сложных алгоритмов и структур данных, обычно как раз и обеспечиваемых компонентами. Вот почему языки сценариев часто называют склеивающими языками (glue languages) или языками интеграции систем (system integration languages).

Бестиповость

Существует тенденция делать языки сценариев бестиповыми для упрощения связывания компонентов. Все сущности выглядят и ведут себя одинаковым образом, а значит могут стать взаимозаменяемыми. Например, в Tcl или Visual Basic переменная может в один момент содержать строку, а в следующий - целое число. Код и данные часто взаимозаменяемы, так что программа может генерировать другую программу и затем исполнять ее. Универсальным способом представления для различных сущностей программы в языках сценариев служат цепочки символов.

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

select | grep scripting | wc

Программа select читает текст, выбранный для вывода на дисплей, и выдает его в качестве своего выхода; программа grep, получив его на входе, выдает на выход строки, содержащие слово "scripting"; наконец, программа wc подсчитывает количество строк на своем входе. Каждая из этих программ может быть использована во многих других ситуациях при выполнении разнообразных задач.

Сильно типизированная природа языков программирования систем не способствует повторному использованию кода, а заставляет программистов создавать массу однотипных интерфейсов, каждый из которых рассчитан на объекты специфических типов. Что же до объектов других типов, то компилятор запрещает их использование с этими интерфейсами, даже если это было бы полезно. Поэтому, чтобы использовать новый объект с существующим интерфейсом, необходимо запрограммировать преобразование типа этого объекта в допустимый тип. Это в свою очередь потребует перекомпиляции части или всего приложения, но поскольку многие приложения распространяются в двоичной форме, подобная модификация оказывается попросту невозможной.

Чтобы оценить по достоинству бестиповые языки, рассмотрим следующую команду на Tcl:

button .b -text Hello! -font (times 16) - command (puts hello)

Эта команда создает новую управляющую кнопку, предназначенную для отображения цепочки символов шрифтом Times размером 16 кеглей, и печатает короткое сообщение, когда пользователь щелкнет по этой кнопке. Команда смешивает шесть разных типов сущностей в один оператор: имя команды (button), управление кнопкой (.b), имена свойств ( -text, -font, -command), простые строки (Hello! и hello), описание шрифта (Times 16), включающее имя гарнитуры (Times) и размер (16), и, наконец, еще одну Tcl-команду (puts hello). Все эти сущности представляются единообразно, посредством цепочек символов. В этом примере свойства могут задаваться в любом порядке, а неспецифицированным свойствам дается значение по умолчанию.

При реализации на Java тот же пример требует 7 строк кода в двух методах. При использовании C++ и библиотеки базовых классов MFC потребуется около 25 строк в трех процедурах [1]. Только установка шрифта требует нескольких строк:

Cfont *fontPtr = new Cfont();
fontPtr -> CreateFont(16, 0, 0, 0, 700, 0, 0, 0, 
  ANSI_CHARSET,
  OUT_DEFAULT_PRECIS,
  CLIP_DEFAULT_PRECIS,
  DEFAULT_QUALITY,
  DEFAULT_PITCH|FF_DONTCARE,
  "Times New Roman");
buttonPtr -> SetFont(fontPtr);

Большая часть этого кода - следствие сильной типизации. Для установки шрифта кнопки управления должен быть вызван метод SetFont, но этому методу в качестве аргумента следует передать указатель на объект Cfont, что, в свою очередь, требует, чтобы новый объект был объявлен и инициализирован. Для инициализации объекта Cfont надо вызвать его метод CreateFont, но он имеет жесткий интерфейс, требующий задания 14 аргументов. В Tcl же существенные характеристики шрифта можно использовать без каких-либо деклараций или преобразований. Кроме того, Tcl позволяет включить поведение управляющей кнопки прямо в команду, которая эту кнопку создает, в то время как на C++ и Java для этого приходится писать отдельно декларируемый метод. (На практике с таким примером, вероятно, проще всего было бы справиться с помощью графической среды разработки, которая скрывает от пользователя сложность базового языка. Пользователь просто вводит величины свойств в форму, и среда разработки генерирует код. Однако в более сложных ситуациях, таких как назначение величин свойств в зависимости от различных условий или при необходимости генерировать интерфейсы "на лету", программисту все равно приходится оперировать базовым языком).

Может показаться, что бестиповая природа языков сценариев не позволяет своевременно распознавать ошибки, но на практике такие языки оказываются столь же безопасными, как и языки программирования систем. Допустим, в примере с кнопкой размер шрифта задан бессмысленной цепочкой наподобие 'IO'. Языки сценариев способны обнаружить такую ошибку в самый последний момент, при выполнении команды. Сильная типизация позволяет выявлять ошибки в процессе компиляции, так что в проверках во время исполнения уже нет нужды. Однако платой за эффективность являются жесткие ограничения, которые приводят к усложнению кода и снижению гибкости программ.

Интерпретируемость

В то время, как языки программирования систем обычно являются компилируемыми, языки сценариев почти всегда интерпретируемые. Когда постоянная перекомпиляция не требуется, процесс разработки идет быстрее. Интерпретаторы делают его более гибким, позволяя вносить изменения в программы непосредственно во время их исполнения. Например, многие инструментальные среды для синтеза и анализа интегральных схем включают интерпретатор Tcl; в них сценарии на Tcl используются как для специфицирования исследуемых объектов, так и для управления работой самого инструментария. Интерпретаторы позволяют реализовать такой мощный метод, как генерация программы "на лету" - во время исполнения. Например, опирающийся на Tcl браузер способен выполнять синтаксический разбор HTML-страницы, транслируя ее в сценарий на Tcl путем применения нескольких последовательных подстановок регулярных выражений. Затем этот сценарий исполняется и страница появляется на экране.

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

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

Языки сценариев имеют более высокий уровень, чем языки программирования систем в том смысле, что один оператор в среднем способен выполнить намного больше работы (это сотни или даже тысячи машинных команд), в то время как оператор в языках программирования систем выполняет около пяти машинных команд (рис. 1). Частично эта разница опять же связана с интерпретируемой природой языков сценариев, но в основном определяется тем, что элементарные операции в нем обладают более богатой функциональностью. Например, в языке Perl инициировать подстановку регулярных выражений так же легко, как выполнить сложение целых чисел. В Tcl переменную легко трассировать с одновременным учетом возникающих побочных эффектов, что, в частности, можно использовать для непрерывного обновления ее значения на экране.

Picture 1.

Рисунок 1.
Сравнение языков на основе уровня программирования и силы типизации

Языки сценариев особенно удобны для быстрой разработки приложений - таблица 1, быть может не слишком строго, но наглядно иллюстрирует этот факт. В таблице описаны несколько приложений, либо сначала реализованные на языке программирования систем, а затем переписанные на языке сценариев, либо наоборот. "Сценарная" версия во всех случаях требовала меньше кода и времени разработки; коэффициент, выражающий эту разницу, варьировался от 2 до 60. Сценарные языки были менее эффективны, когда использовались для первой реализации; впрочем, и без того очевидно, что новая реализация всегда выигрывает от осмысления первого опыта. Так или иначе, от 5 до 10 - более правдоподобное значение коэффициента, выражающего разницу между написанием сценария и программированием систем, чем приведенные в таблице экстремальные значения. Результаты зависят, естественно, и от разрабатываемого приложения. В последнем примере из таблицы часть приложения, связанная с графическим пользовательским интерфейсом, реализована с помощью технологии склейки - в отличие от собственно моделирующей части; это объясняет, почему в данном случае сценарный подход обеспечил меньше преимуществ, чем в других приложениях.

Приложение Сравнение Фактор "кода" * Фактор "усилий"** Комментарии
База данных (Кен Кори) Версия на Tcl: 1 день Версия на C++: 2 месяца нет данных 60 Версия на С++ реализована первой; версия Tcl обладает большей функциональностью
Тестирование и установка компьютерной системы (Энди Белси) Тест на Cи: 272 тыс. строк, 120 месяцев; приложение на Си: 90 тыс. строк, 60 месяцев Версия на Tcl/Perl: 7700 строк, 8 месяцев 47 22 Версия на Си реализована первой; версия на Tcl/Perl заменила оба приложения на Си
Библиотека для базы данных (Кен Кори) Версия на С++: 2-3 месяца Версия на Tcl: 1 неделя нет данных 8-12 Версия на С++ реализована первой
Сканирующая программа защиты (Джим Грэхам) Версия на Си: 3000 строк Версия на Tcl: 300 строк 10 нет данных Версия на Си реализована первой; версия на Tcl имела большую функциональность
Графики добычи нефти (Дан Шенк) Версия на Си: 3 месяца Версия на Tcl: 2 недели нет данных 6 Версия на Tcl реализована первой
Диспетчер очередей (Пол Хили) Версия на Си: 1200 строк, 4-8 недель Версия на Tcl: 500 строк, 1 неделя 2,5 4-8 Версия на Си реализована первой, не имела комментариев; версия на Tcl обладает большей функциональностью и снабжена комментариями
Электронные таблицы Версия на Си: 1460 строк Версия на Tcl: 380 строк 4 нет данных Версия на Tcl реализована первой
Система моделирования и графический пользовательский интерфейс (Рэнди Ванг) Версия на Java: 3400 строк, 3-4 недели Версия на Tcl: 1600 строк, меньше 1 недели 2 3-4 Версия на Tcl обладала примерно на 10-20% большей функциональностью и была реализована первой
* Фактор "кода" есть отношение строк кода для двух реализаций
** Фактор "усилий" есть отношение времен разработки (в большинстве случаев две версии реализовывались различными людьми)

Информация в таблице была получена от нескольких разработчиков на Tcl в ответ на статью, помещенную в comp.lang.tcl newsgroup.

Разные средства для разных задач

Языки сценариев не могут заменить языки программирования систем (впрочем, верно и обратное). Каждый из двух типов языков имеет собственную нишу. Если при создании приложений необходимо склеивать компоненты и интегрировать системы, то языки сценариев позволят сделать это в 5-10 раз быстрее. В то же время особенности языков программирования систем облегчают реализацию сложных алгоритмов и структур данных. Кроме того, языки программирования систем позволяют писать программы в 10-20 раз более быстрые, чем языки сценариев, поскольку содержат намного меньше операций проверки во время исполнения.

Можно сделать вывод, что оба типа языков должны использоваться в сочетании друг с другом. Например, системные программисты пишут компоненты ActiveX на Си, а их менее искушенные коллеги могут затем использовать эти компоненты при создании приложений с помощью Visual Basic. В Unix несложно писать командные файлы на shell, инициирующие работу приложений на Си. Одна из причин популярности Tcl - его способность к расширению путем создания новых команд, реализованных на Си.

Рост значимости языков сценариев

Итак, языки сценариев существуют долгие годы, но с недавних пор они начали играть более важную роль. Это стало результатом действия нескольких факторов. Самый важный из них - переход от простой "смеси" совместно используемых, но отдельно функционирующих приложений к "склеивающим" приложениям. Три слагаемых этого изменения: графические пользовательские интерфейсы, Internet и компонентные интегрированные среды.

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

Мне не приходилось слышать о каких-либо средах быстрой разработки графических пользовательских интерфейсов на основе языка программирования систем. В случае с Windows, Macintosh Toolbox или Unix Motif подобные средства, опирающиеся на Си или C++, всегда оказывались тяжелыми для изучения, неудобными и негибкими. В некоторых из перечисленных систем неплохие графические средства для проектирования экрана скрывают базовый язык - но не в тех случаях, когда дизайнеру приходится программировать, чтобы сгенерировать поведение для элементов интерфейса. Все лучшие среды быстрой разработки графических пользовательских интерфейсов основаны на языках сценариев: Visual Basic, HyperCard и Tcl/Tk.

Экспансия Internet во многом способствовала популяризации языков сценариев. В конце концов, Сеть - не что иное, как склеивающий инструмент. Она не создает новых данных или вычислительных процедур, а просто делает огромное множество уже существующих ресурсов более доступными. Идеальным для большинства задач в Internet был бы язык, способный обеспечить совместную работу всех связанных компонентов - язык сценариев. Например, Perl стал популярным средством для написания сценариев графических пользовательских интерфейсов, а JavaScript оказался удобным инструментом для сценарной разработки Web-страниц.

Третий пример сценарно-ориентированных приложений - компонентные интегрированные среды, такие как ActiveX и JavaBeans. Без хорошего языка сценариев, прямо предназначенного для манипулирования компонентами, потенциал интегрированных сред в значительной мере не получит реального воплощения. Не удивительно, что интегрированные среды завоевали столь большую популярность на ПК - ведь именно на этой платформе Visual Basic обеспечивает удобное средство для сценарно- ориентированных технологий, а на других платформах, например, Unix/CORBA, такого рода средства в компонентную интегрированную среду в явном виде не включены.

Еще одна причина популярности языков сценариев коренится в значительном прогрессе сценарно-ориентированных технологий. Современные языки сценариев Tcl и Perl далеко ушли от своих предшественников. Например, JCL не обеспечивал даже базового взаимодействия, а ранний shell не поддерживал процедур. Впрочем и сегодня сценарные технологии не достигли полной зрелости. Например, Visual Basic не является полноценным языком сценариев - он изначально проектировался как простой язык программирования систем и только затем приобрел свойственные языку сценариев черты.

Важный стимул для совершенствования сценарных технологий - постоянно увеличивающаяся производительность компьютеров. Еще недавно казалось, что единственный способ получить высокопроизводительное приложение - создать его на языке программирования систем, а то и на языке ассемблера. Однако, по сравнению с машинами двадцатилетней давности производительность увеличилась в 100-500 раз и продолжает удваиваться каждые 18 месяцев. Сегодня многие приложения, реализованные на интерпретируемом языке, имеют отличную производительность. Например, сценарий на Tcl может манипулировать множеством в несколько тысяч объектов и при этом обеспечивать приемлемое время отклика. Чем производительнее компьютеры, тем привлекательнее сценарно-ориентированные технологии для создания больших и сложных приложений.

Программисты по случаю

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

Программисты "по случаю" не склонны тратить месяцы на изучение языка, а хотели бы освоить его за несколько часов. В случае языков сценариев это вполне реально. Их легче осваивать, поскольку у них простой синтаксис и обычно опущены сложные конструкции наподобие объектов и потоков. Сравните Visual Basic и Visual C++ - мало кому из непрофессионалов может прийти сама мысль об использовании Visual C++, но очень многие с успехом пишут на Visual Basic.

Уже сегодня количество приложений, написанных на языках сценариев, превосходит число тех, что написаны на языках программирования систем. Это верно и для Unix-систем, в которых функционирует намного больше сценариев на shell, чем программ на Си; и для Windows, где больше приложений на Visual Basic, чем на Си или C++. Конечно, большинство сложных и наиболее распространенных приложений по-прежнему создается на языках программирования систем, так что сопоставление общего количества строк кода или числа установленных копий пока еще говорит в их пользу. Однако, языки сценариев стали основной движущей силой в разработке приложений, и их доля на рынке будет возрастать.

Роль объектов

Языки сценариев не избалованы вниманием экспертов в области языков и программной инженерии, которые сфокусировались на объектно-ориентированных языках программирования систем C++ и Java. Широко распространилось мнение, что именно объектно-ориентированное программирование представляет собой следующий шаг в эволюции языков. Утверждается, например, что такие его особенности, как сильная типизация и наследование сокращают время разработки, усиливают степень повторного использования компонентов и решают многие другие проблемы, включая и те, которые стоят перед языками сценариев.

Но действительно ли выгоды от объектно-ориентированного программирования столь велики? К сожалению, я пока еще не видел надежных количественных данных, чтобы с уверенностью дать утвердительный ответ на этот вопрос. По моему мнению, использование объектов способно принести достаточно скромные дивиденды: возможное увеличение в производительности я оцениваю в диапазоне 20-30%, а вовсе не в два и тем более не в 10 раз. Хорошо известно противоречивое отношение программистского сообщества к C++, а некоторые влиятельные эксперты начинают все громче высказываться вообще против объектно-ориентированного программирования как такового [8]. В любом случае, объекты не увеличивают производительность столь существенно, как сценарная технология, а обеспечиваемые ими преимущества могут быть достигнуты и с помощью языков сценариев.

Объектно-ориентированное программирование не обеспечивает качественного скачка в производительности, так как, в сущности, не повышает уровень программирования, да и не слишком благоприятствует повторному использованию. В таких языках, как С++, программисты, как и раньше, работают с небольшими базовыми элементами, которые должны быть описаны во всех их деталях. В принципе, мощные библиотечные модули могли бы разрешить эту проблему; если бы библиотеки использовались действительно широко, они могли бы повысить уровень программирования. Но на сегодняшний день таких библиотек немного. Сильная типизация большинства объектно-ориентированных языков делает модули весьма специализированными, затрудняя их повторное использование; для совместной работы модулей часто приходится программировать преобразования, транслирующие один тип в другой.

Другая проблема объектно-ориентированных языков - их акцент на наследовании. "Наследование реализации", при котором один класс заимствует код, написанный для другого, - весьма сомнительная идея, приводящая к проблемам при разработке и повторном использовании: реализации классов привязываются друг к другу, так что ни один класс не может быть понят без другого. Подкласс нельзя понять без знания того, как унаследованные методы реализованы в его суперклассе, а суперкласс, в свою очередь, нельзя понять, не зная, какие из его методов наследуются в подклассах. В сложной иерархии классов, ни один отдельный класс не может быть понят без понимания всех других классов в иерархии. Что еще хуже, класс нельзя безболезненно выделить из иерархии для повторного использования. Множественное наследование делает проблему еще более тяжелой. Наследование реализации вызывает взаимное "переплетение" классов и их уязвимость, сходные с теми, что наблюдались, когда сверх меры использовали оператор goto.

Языки сценариев фактически ввели в действие принцип повторного использования. Модель, которой они следуют при создании приложений, предполагает, что необходимые компоненты могут быть встроены в язык программирования систем и затем склеены вместе с помощью языка сценариев. Такое "разделение труда" обеспечивает естественную схему для повторного использования. Компоненты проектируются в расчете на повторное использование, что и реализуется с помощью сценариев, использующих хорошо определенные интерфейсы между компонентами. В Tcl компонентами являются настраиваемые пользователем команды, реализуемые на Си; они выглядят подобно встроенным командам, поэтому их легко использовать в сценариях Tcl. В Visual Basic в качестве компонентов служат элементы ActiveX.

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

К счастью, сильные стороны объектов в языках программирования систем могут быть достигнуты и в языках сценариев, практически каждый из которых в той или иной степени поддерживает объектно-ориентированное программирование. Например, Python является объектно-ориентированным языком сценариев; последняя версия Perl включает поддержку объектов; Object Rexx является объектно-ориентированной версией Rexx, а Incr Tcl - объектно-ориентированное расширение Tcl. При этом сохраняется присущая языкам сценариев бестиповость объектов.

Другие языки

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

Заключение

Итак, языки сценариев предлагают иные по сравнению с языками программирования систем приоритеты: они пренебрегают заботой об эффективности и силе типизации, но значительно повышают продуктивность труда программистов и поощряют повторное использование. Это приобретает все больший смысл по мере того, как компьютеры становятся быстрее и дешевле, а программистский труд - дороже. Языки программирования систем хорошо подходят для создания компонентов, реализующих сложные алгоритмы и структуры данных, в то время как языки сценариев ориентированы на склеивающие приложения, где сложность скрыта в компонентах. "Склеивающая" технология становится все более распространенной при создании приложений; вот почему языки сценариев станут ведущей парадигмой программирования в 21 веке.

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

Литература

  1. J. Ousterhout, "Additional Information for Scripting White Paper", - http://www.scriptics.com/people/john.ousterhout/scriptextra.html.
  2. C. Jones, "Programming Language Table, Release 8.2", - Mar. 1996, - http://www.spr.com/library/0langtbl.htm.
  3. B. Boehm, "Software Engineering Economics", - Prentice Hall, Englewood Cliffs, NJ, 1981.
  4. L. Wall, T. Christiansen, and R. Schwartz, "Programming Perl", 2nd ed., - O'Reilly & Associates, Sebastopol, CA, 1996.
  5. M. Lutz, "Programming Python", - O'Reilly & Associates, Sebastopol, CA, 1996.
  6. R. O'Hara and D. Gomberg, "Modern Programming Using REXX", - Prentice Hall, Englewood Cliffs, N, 1988.
  7. J. Ousterhout, "Td and Tk Toolkit", - Addison-Wesley, Reading, MA, 1994.
  8. S. Johnson, "Objecting To Objects", - Invited Talk, Usenix Technical Conf., San Francisko, Jan. 1994.

Об авторе: Джон Остераут (John Ousterhaut) - основатель и руководитель компании Scriptics. До недавнего времени работал в Sun Microsystems, где возглавлял проект SunScript. Его интересы включают сценарные языки, программирование для Internet, пользовательские интерфейсы и операционные системы. Джон известен в России как создатель языка сценариев Tcl и инструментария Tcl toolkit. Остераут удостоен многих престижных наград, в том числе премии ACM Grace Murray Hopper Award и Президентской премии Национального научного фонда США.

John K. Ousterhout "Scripting: Higher-Level Programming for the 21st Century", - Computer, Vol.31, N.3, March 1998. Reprinted with permission, Copyright IEEE CS. All rights reserved.


Выбор типа языка

  • Является ли связывание уже существующих компонентов приоритетной задачей при построении приложения?
  • Будет ли приложение манипулировать несколькими объектами разной природы?
  • Включает ли приложение развитый графический пользовательский интерфейс?
  • Предполагается ли в рамках приложения много манипулировать цепочками символов?
  • Будет ли функциональность приложения меняться с течением времени?
  • Должно ли приложение быть расширяемым?

Если ответы на эти вопросы утвердительные, стоит подумать об использовании языка сценариев. С другой стороны, утвердительные ответы на следующие вопросы означают, что лучше использовать язык программирования систем.

  • Будут ли в приложении сложные алгоритмы или сложные структуры данных?
  • Предполагаются ли манипуляции с большими массивами данных, например, со всеми пикселами изображения, при которых производительность становится критичной?
  • Являются ли функции приложения хорошо определенными и мало подверженными изменениям?

В течение последние 30 лет в большинстве основных вычислительных платформ применялись средства обоих типов. Например, одним из первых языков сценариев был Job Control Language (JCL), использовавшийся для управления последовательностью шагов задания в OS/360. Сами же по себе отдельные шаги задания писались на PL/1, Фортране или языке ассемблера. В 80-х гг. на Unix-машинах для системного программирования служил Си, а для задания сценариев обработки - оболочки sh и csh. С наступлением эры ПК лидерами в сфере программирования систем стали Си и C++, а ведущим языком сценариев - Visual Basic. Наконец, в формирующемся на наших глазах мире Internet приоритетным языком программирования систем считается Java, а разработка сценариев ведется на JavaScript, Perl и Tcl.