Обзоры
Инструменты скриптинга
18

Инструменты скриптинга

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


Мы пирог свой зажарим на чистом сале,
ибо так вкуснее: нам так сказали.

И. Бродский

Такое лирическое отступление с весьма прозаичным смыслом вполне оправдано
— еще недавно отвергаемая в стане одного из гигантов технология скриптинга начинает
овладевать умами. Уже слышимая и видимая битва Java и C# — лучшее тому подтверждение.
Но давайте попробуем услышать и Поэта, на время забыв о "чистом сале".
Попытаемся обратить свой взгляд на тех самых "маленьких людей", строящих
большие пирамиды.

Естественно, что рассказать о всех достижениях в области скриптинга не позволяет ни объем статьи, ни даже объем журнала, поэтому представляемый вниманию читателей материал ни в коей мере не претендует на объективность — в конце концов, все мы в словах Поэта вправе услышать "обращение к себе лично". И тем не менее… Все тщательно отобранные для этого обзора языки обладают тремя основными чертами — они доступны всем (т. е. бесплатны), они работают чуть ли не на всех платформах (включая, естественно, mainstream — MS WIndows и Unix), и наконец, они интересны и просты в изучении.

Почему именно скриптинг?

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

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

Пояснить все вышесказанное можно на надуманном (но не оторванном от реалий суровой программистской действительности) абстрактном примере реализации, скажем, программы визуализации экспериментальных данных. Предположим, что в инструментальном наборе есть простая, быстрая и надежная библиотека реляционной СУБД (таких в природе существует множество, нашу абстрактную мы назовем DB), утилита визуализации двумерных данных (на человеческом языке — черчения графиков, присвоим ей имя Draw2D), аналогичная ей утилита визуализации трехмерных данных (Draw3D). Кем, когда и даже как написаны все эти программы — нам не важно, мы должны знать только их требования ко "входам" и "выходам". "Кирпичи" у нас есть, дело осталось за "цементом" — пусть это будет "самый лучший в мире скриптовый язык" Best (которого, естественно, нет). Теперь строим нашу маленькую "пирамиду" — программу на языке Best: с помощью средств Best указываем, что мы хотим использовать сторонние программы (DB, Draw2D, Draw3D), создаем объекты Best "сверхвысокого уровня", отвечающие за управление базой данных и процессами "рисования", расширяем сам язык Best новыми конструкциями, отражающими функционирование объектов "сверхвысокого уровня" (на основе возможностей DB, Draw2D, Draw3D), создаем "объекты-связники" и, наконец, фильтры, "подгоняющие" форматы данных, передаваемых через "связники", под требования соответствующих сторонних программных компонентов. Собственно говоря, все.

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

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

Icon и Idol

В мире скриптинга эти две взаимосвязанные разработки представляют собой
настоящий концентрат лучших идей, далеко не блещущих новизной. Основной язык —
Icon — является последователем исключительно мощного семейства SNOBOL, первые
представители которого создавались в знаменитых Bell Labs еще в начале 60-х годов.
Несмотря на почтенный возраст, Icon обладает функциональностью, немыслимой для
модных сегодня систем. Хотя судите сами…

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

Среда разработки Icon

Во-первых, встроенные типы данных Icon оставляют далеко позади все прочие языки:
строки в нем являются примитивным типом (равноправным с традиционными integer
и float), есть немало операций над ними, есть встроенный тип cset (множество символов),
успешно конкурирующий с регулярными выражениями Unix в операциях поиска подстрок,
процедуры относятся к величинам первого класса (first class values, что означает
возможность присваивания переменным значений… самих процедур, а не результатов
их выполнения), списки, записи, ассоциативные таблицы, множества по модулю 2 (в
которых автоматически устраняется дублирование элементов)… Во-вторых, в Icon
встроены реализации замечательного механизма "совыражений" (автор позволил
себе кальку труднопереводимого co-expression) — почти независимо выполняющихся
частей программы, что дает возможность создавать как классические сопрограммы,
так и реализовывать произвольные псевдопараллельные алгоритмы. В-третьих, Icon
использует прозрачную реализацию мощной концепции бэктрекинга (отката, о чем чуть
позже). В-четвертых, язык поддерживает автоматическую конверсию типов: присваивание,
например, значения числа строке никак не отличается с точки зрения программиста
от присваивания числа числу. В-пятых, последние версии Icon включают кросс-платформенные
графические возможности. В-шестых, все это буйство возможностей, работающее очень
надежно, что подтверждается версией (текущая — 9.3), отзывами многочисленных
поклонников и, наконец, личным опытом автора, укладывается в скромный 1 MB упакованных
исходных текстов.

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

procedure main()
write("Hello, world")
end

Но за синтаксической простотой скрывается огромная пропасть, отличающая мощную семантику Icon от большинства традиционных языков просто высокого уровня. Как и большинство настоящих скриптовых языков, Icon — безтиповый, т. е. переменные в нем не имеют типа и играют роль контейнеров, способных хранить либо гарантированно уникальное значение null (ничего), либо значение любого типа. Сам же тип является атрибутом величины (значения). Кроме того, Icon — "выразительный язык", но не в синтаксическом смысле. Почти все, что есть в нем, — это выражения, способные возвращать значение. Такая особенность позволяет создавать, например, немыслимые в традиционных языках управляющие конструкции (правда, нездоровое увлечение "выразительностью" приводит и к конструкциям, которые невозможно понять). Ну и, наконец, главная "изюминка" Icon — вычисления, управляемые целью (goal-directed evaluation). Программистам, привыкшим к тому, что в управляющих конструкциях языка (например, if, while, for, Loop) выражения сравнения принимают два значения — "истина" или "ложь", придется переучиваться. В Icon любое выражение может быть вычислено либо успешно, либо неуспешно. В первом случае результатом успеха становится вычисленное значение выражения, а вот во втором вступает в действие механизм бэктрекинга — интерпретатор Icon обращается к предыдущему выражению, на основе результатов вычисления которого выполнялось текущее, и пытается найти новое его значение. Этот процесс рекурсивный и будет повторяться до тех пор, пока цель вычисления не будет достигнута.

Рассказывать об Icon и его объектно-ориентированном расширении Idol можно еще
очень и очень много, но в нашем списке этот язык не один. Заинтересовавшимся же
можно порекомендовать отличную online-версию книги
Icon Programming Language Handbook Томаса
Кристофера (пригодный к чтению на
любых платформах pdf-формат) и сами системы Icon
и Idol
, распространяемые в исходных текстах. В любом случае изучение этих
языков доставит удовольствие и вооружит вас мощнейшим инструментом как быстрого
прототипирования сложных программ, так и "реактивного" создания целого
класса очень нужных приложений, для которых любой другой инструментарий не подходит
из-за слишком больших трудозатрат.

Ruby

В отличие от буквально сногсшибательной нетрадиционности Icon, японская
разработка под названием Ruby предлагает более прагматичный подход и ориентирована,
в первую очередь, на программистов — приверженцев объектно-ориентированного программирования.
Сегодня Ruby можно смело называть фаворитом, ему посвящаются восторженные статьи,
он привлекает внимание таких компаний, как Sun и IBM.

Среда изучения и разработки
Ruby- программ для MS Windows

И действительно, "скриптинг по-японски" хорош. Ruby — чистый объектно-ориентированный
(ОО) язык (без встроенных примитивных типов, в отличие от Java и C++), в нем все,
что есть, — это классы и объекты. Как и Icon, Ruby — язык бестиповый, в том
смысле, что переменные в нем не имеют типов, тип является атрибутом значений.
Реализация ОО механизмов в нем — динамическая, что также выгодно отличает Ruby
от статических конкурентов. Синтаксис языка наследует многие черты традиционных
ЯВУ (языков высокого уровня), что сокращает время изучения. Непременный элемент
современных ОО языков — сборщик мусора (garbage collector) — работает очень
устойчиво и быстро и, что главное, освобождает программиста от "головной
боли" с отслеживанием использования памяти. В язык встроена мультиплатформенная
поддержка поточного программирования, что, в отличие от Java, почти не сказалось
на сложности реализации интерпретатора (размеры дистрибутива Ruby в исходных текстах
— около 800 KB). Как и в Java, в Ruby нет таких потенциально опасных элементов,
как указатели, но сторонники программирования с использованием pointer, небезосновательно
считающие, что устранение указателей ведет к потере производительности, могут
быть спокойны. Мощная (и в своем роде — уникальная) реализация итераторов в Ruby
почти не имеет себе равных. Приятно радует в Ruby любителей точных вычислений
то, что объекты класса Bignum представляют собой целые числа… произвольной точности.

В целом же, оснащенность библиотеки языка очень высока — здесь есть чуть ли не все, а врожденная приспособленность к расширению вызывает к жизни ежедневно десятки проектов, адаптирующих Ruby к новым областям применения. Из необязательных, но приятных нюансов следует отметить независимую от платформы реализацию регулярных выражений Unix (что позволяет без изменения использовать, например, CGI-скрипты на Ruby как в Windows, так и в Unix), очень неплохие показатели быстродействия интерпретатора, отличный базовый инструментальный набор (например, отладчик), чистоту синтаксиса и семантики языка (перекликающихся, к слову, с Ada и Eiffel).

В целом, Ruby намного "привычнее" Icon и Idol и легче в освоении, но
и намного моложе, что сказывается на "альфа-версийном" характере большинства
расширений. Это, конечно, не недостаток, да и для обоих языков можно найти прекрасные
ниши применения. Как интегративное средство Ruby выглядит более привлекательно
— это обусловлено упрощенными внутренними интерфейсами для программ-расширений,
создаваемых на языке C, и возможностью динамической загрузки и использования сторонних
библиотек (естественно, на тех платформах, где такая возможность представлена
системным ПО). Реализация языка с исключительно либеральной лицензией, допускающей
бесплатное использование даже в коммерческих программах, доступна на сайте www.ruby-lang.org.
Здесь же сконцентрированы и ссылки на основные сетевые ресурсы, связанные с Ruby,
и минимально необходимая документация. А уж сам язык, безусловно, заслуживает
и нетрудной процедуры загрузки небольших файлов из Сети, и изучения — это достойный
конкурент как признанным "грандам скриптинга" (Perl, Python, которых,
по крайней мере в Японии, Ruby почти окончательно и полностью победил), так и
коммерческим разработкам (среди которых полноценных аналогов Ruby не наблюдается).


"Крошка Lua"

В
отличие от достаточно "больших" Icon и Ruby, крошка Lua, казалось бы,
особенного интереса представлять не может. Действительно, что же это за язык такой,
реализация которого в исходных текстах занимает всего… неполных 150 KВ, а размер
исполняемой версии простейшей командной оболочки Lua — 40 KB. Но оказывается,
что в Бразилии (стране, где разработан Lua) не только "много диких обезьян".
И бразильские программисты могут успешно "воевать" со своими японскими
и американскими коллегами. Потому что "крошка Lua" является настоящим
концентратом концепций.

"Безумный Иван"
— робот-чемпион под управлением Lua

Начнем с того, что Lua — язык не самостоятельный. Его разработчики не стремились
создать нечто, способное существовать за пределами некоторой высокоуровневой программы.
Эта особенность сохранилась до последних версий Lua, но вот понятие "высокоуровневой
программы" со временем уменьшилось до… 5 строк на языке C. Как и все предыдущие
языки, Lua — бестиповый (тип является атрибутом значения), с Icon его роднит
общий характер функций как величин первого класса. Несмотря на миниатюрность,
набор встроенных типов данных вызывает уважение: обязательный и уникальный nil
(ничего), number — число с плавающей точкой двойной точности, строка string,
функция, ассоциативная таблица table и задаваемый пользователем тип userdata.
Каждый объект определенного типа в Lua содержит скрытый специальный ярлык (tag),
что позволяет в ходе выполнения программы, например, проверять тип значений, хранящихся
в переменных, и в соответствии с ним изменять ход выполнения программы. Способности
к расширению у Lua прекрасные, так как язык создавался именно для целей разработки
расширяемых приложений — так, тип значений function имеет различающиеся ярлыки
для функций, написанных на Lua и реализованных извне на С. Главное отличие Lua
от остальных языков — сквозная поддержка идеологии расширения семантики.

Отладчик Lua-программ

Такой уникальный "обоюдоострый меч" (допускающий одновременно расширение
приложения за счет использования скриптинга и расширение самого языка скриптинга)
превращает Lua в действительно грозное оружие. А "скорострельный" интерпретатор
байт-кодов и отличная инструментальная поддержка (включающая даже сборщик мусора)
делают язык очень привлекательным для разработчиков.

Baldur’s Gates II — игра,
в которой все настройки выполняются с помощью языка Lua

Настоящей жемчужиной Lua можно по достоинству считать идею fallbacks (перевести
это идиоматическое выражение трудно). По сути она напоминает механизм исключительных
ситуаций в традиционных языках программирования — когда в ходе выполнения программы
происходит "что-то не то" (исключительная ситуация), вызывается соответствующая
подпрограмма — обработчик этой ситуации. Но само название "исключительная
ситуация" говорит о том, что это событие, которого не должно быть. Fallback,
напротив, является очень даже ожидаемым событием (а в некоторых случаях — единственно
возможным): когда интерпретатор Lua в ходе выполнения программы "сталкивается"
с попытками применить какую-либо операцию к данным недопустимого типа, или же
прочитать запись из таблицы по ключу, которого в таблице не существует, или даже
вызвать для выполнения содержимое переменной, на деле не являющееся функцией,
— ничего страшного не происходит. На каждую fallback-ситуацию существует свой
обработчик (по умолчанию просто сообщающий об ошибке), который может быть изменен
программистом и реализован как на Lua, так и сторонней функцией на C. С помощью
соответствующих обработчиков fallback, ассоциативных таблиц, определяемых пользователем
типов и функций — величин первого класса, из Lua можно сделать… любой язык.
Какой угодно, и при этом максимально "привязанный" к конкретной прикладной
области.

Исключительно удачная разработка, хорошо документированная, простая, концептуально
целостная, Lua доступен в исходных текстах с
сайта разработчика
— технологической группы компьютерной графики Папского
Католического Университета Рио-де-Жанейро.

За пределами…

Увы, для многих замечательных скриптовых языков места не осталось. Но все
же любители стековых машин найдут отличную реализацию "скриптинг-FORTH"
под названием ficl на
сайте
, сторонникам Lisp-подобной математической строгости придется по душе
одна из многочисленных реализаций
Scheme
, буквально на днях увидевшая свет альфа-версия
интерпретатора
… стандартного C++, несомненно, порадует поклонников этого
языка, не боящихся загружать из Сети десятки мегабайт. Ну и, наконец, любители
"просто С" найдут достойное применение EiC (встраиваемый/расширяемый
интерпретатор С с виртуальной машиной
).


Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: