|
|
Сценарные языки, или языки скриптов (scripting languages), за последние годы сделали огромный шаг вперед. Еще лет десять назад им отводилась роль вспомогательных средств, которые и называть-то языками программирования было как-то неловко. Сейчас же скепсис по отношению к ним сменился интересом и признанием. Но какова их природа и какое место они занимают среди всех языков?
Лисп как предтеча сценарных языков
Характеризуя ситуацию с вавилонским столпотворением языков программирования, Джон Бэкус, автор Фортрана и формы описания синтаксиса BNF (форма Бэкуса—Наура), писал (1977): «По-видимому, с языками программирования происходит что-то неладное. Всякий новый язык включает с небольшими изменениями все свойства своих предшественников плюс кое-что еще. Руководства по некоторым языкам занимают более 500 страниц...
В течение двадцати лет языки программирования неизменно развивались в одном и том же направлении, пока не дошли до нынешнего состояния «ожирения»... Теперь это излюбленная область тех, кто предпочитает возиться с пухлыми перечнями подробностей вместо того, чтобы бороться за новые идеи. Дискуссии о языках программирования часто напоминают средневековые диспуты о числе ангелов, которые могут разместиться на кончике иглы, а не волнующие споры о фундаментально различающихся понятиях... Достойно удивления, почему столь многие из нас, изучив отвратительные структуры типов традиционных языков с помощью изящного инструментария, разработанного Д. Скоттом, пассивно сохраняют верность этим структурам...»
В 1952 г. в швейцарском Базеле была издана работа Х. Рутисхаузера «Автоматическая разработка плана с помощью программно-управляемых вычислительных машин» (Automatische Rechenplanfertigung bei program-gesteuerten Rechenmaschinen, Birkhauser Verlag), с которой и ведет отсчет история изучения языков программирования. За прошедшие полвека появились тысячи языков, различных по своей природе и охватывающих разные модели и парадигмы программирования, включая программирование процедурное, функциональное, символьное, логическое, продукционное, реляционное, параллельное, объектно-ориентированное, модульное (компонентное) и др. Но по сути можно выделить два четких полюса притяжения языков: императивный и декларативный. Именно они и задают основу деления языков. Императивные языки скорее отвечают на вопрос «как?», тогда как декларативные — на вопрос «что?». При этом императивные и родственные им языки обычно носят статический характер, в то время как декларативные имеют динамический.
В 1960 г. возникла идея увеличения общности, заключающаяся в том, чтобы воспользоваться логикой для такого описания фактов, которое не зависело бы от того, как эти факты будут использоваться впоследствии. Джон Маккарти, автор языка Лисп, вспоминает [1]: «Тогда мне казалось (как, впрочем, и сейчас), что люди по объективным причинам предпочитают общаться с помощью декларативных предложений, а не языков программирования, все равно, является ли субъект общения человеком, существом с Альфы Центавра или компьютерной программой... Основным преимуществом декларативной информации является ее общность».
Дж. Бэкус (IBM) и Дж. Маккарти (Массачусетский технологический институт), создавшие знаменитые языки Фортран (1954, Fortran) и Лисп (1958, Lisp) соответственно, задали точку отсчета эволюции языков программирования. Но если традиционные языки, пошедшие по стопам императивного Фортрана, известны достаточно хорошо и применяются весьма интенсивно, то языки другого направления с течением времени стали уходить в тень. Дж. Саммит, известный специалист по языкам программирования, как-то заметила, что все языки программирования грубо можно разбить на два класса. В одном находится Лисп, а в другом — все остальные. С этим трудно не согласиться, ведь Лисп и тогда, и сейчас заметно отличается от традиционных языков своими синтаксисом и семантикой, природой и реализацией.
Бестиповый язык Лисп задумывался Дж. Маккарти как средство для рекурсивных построений. В его основе лежит строгий математический аппарат лямбда-исчисления Чёрча, алгебра списочных структур (S-списки) и теория рекурсивных функций. Процедуры в нем могут служить в качестве данных, подставляемых на место других аргументов. В результате каждого действия возникает некое значение. Значения становятся аргументами следующих действий и т. д. Композиция и рекурсия — основные средства функционального программирования. Необычный синтаксис (скобочная префиксная запись) стал одной из причин сравнительно редкого использования Лиспа и его потомков в наши дни. В этом языке впервые нашли свое воплощение идеи интерпретатора, сборщика мусора, перегрузки операций и других хорошо сейчас известных механизмов. Числовая и символьная обработки стали одними из первых классов задач, для которых создавались языки программирования. Но если Фортран ориентируется на работу с числами, Си — на работу с символами и указателями, то Лисп — на работу с программами. Его структуры данных особенно полезны для представления и манипулирования исходными текстами программ. Это дает огромный потенциал для развития идей, но нередко затрудняет понимание сути для людей непосвященных.
Менее известен тот факт, что Лисп создал почву (идейную и операционную) для появления объектно-ориентированного программирования (ООП), ставшего едва ли не обязательной парадигмой для современных сценарных языков. Принято считать, что истоки ООП лежат в языках Симула (Кристен Нигаард, Оле-Йохан Даль, 1966) и Smalltalk (Алан Кей, 1971). Однако крестный отец языка Паскаль, известный английский ученый Тони Хоар, отмечая сильное влияние Лиспа на свои идеи концепции атрибутивных классов (record classes), уже в 1966 г. достаточно четко изложил суть ООП. Он писал [2]: «Фундаментальная особенность нашего понимания мира заключается в том, что мы систематизируем свой жизненный опыт, представляя его в виде отдельных понятий или объектов (столы и стулья, банковские займы и алгебраические выражения, многочлены и люди, транзисторы и треугольники и т. п.), и наше мышление, язык и действия основываются на обозначении, описании и манипуляциях с этими объектами, с каждым в отдельности или в связи с другими объектами... Всякий объект, представленный в запоминающем устройстве вычислительной машины в виде записи (record), будет обладать одним или более атрибутами (attributes), с которыми и приходится иметь дело при решении задачи... Объекты реального мира часто удобно классифицировать с помощью определенного числа классов (classes), причем любой класс обозначается некоторым собирательным именем, таким как «человек», «банковский заем», «выражение» и т. д.
Лисп отличается не только возможностями унификации представления данных в виде списков, создания основ работы с классами и объектами, а также оперирования с функциями в рамках лямбда-исчисления, но и наличием мощного аппарата макропрограммирования. По сравнению с Лиспом препроцессор языка Си крайне ограничен: его макроязык состоит только из подстановок и конкатенации символов. В нем нет ни рекурсии, ни метарекурсии, другими словами, макросы в Си не могут ни вызывать себя, ни определять другие макросы. Лисп же сам является метаязыком и способен решать сложнейшие задачи трансформации текста.
Недостатки сценарных языков. Заблуждения и стереотипы
Итак, Лисп, по всей видимости, был праотцем сценарных языков. Но что же такое сценарные языки? Это, пожалуй, ключевой вопрос, ответить на который отнюдь не просто. В отношении сценарных языков уже сформировались ложные стереотипы. В частности, это касается таких критериев оценки, как компиляция/интерпретация кода, система типов, быстродействие, требования к памяти, надежность. Склонность к поддержке интерпретации, а не компиляции кода считается едва ли не первым признаком сценарных языков. Интерпретаторы проще в исполнении, нежели компиляторы, и к тому же покрывают более широкий спектр языков. Однако те же традиционные языки Лисп, Снобол, Пролог, Форт и даже «пограничный» Бейсик чаще всего имеют реализации в виде интерпретаторов. Тогда как среди сценарных языков, хоть и нечасто, но можно встретить компиляторы. Например, на платформе Microsoft .NET реализованы компиляторы Perl и Python, порождающие промежуточный MSIL-код, исполняющая (с динамической компиляцией) в рамках среды Common Language Runtime. В настоящее время все чаще используют смешанные схемы, когда код частично компилируется, частично интерпретируется (это свойственно сценарным языкам в индустрии компьютерных игр).
Принято считать, что сценарные языки либо имеют слабую типизацию, либо вообще бестиповые. Это справедливо для части языков, но далеко не для всех. Более того, можно привести примеры бестиповых языков, не являющихся сценарными, взять хотя бы тот же BCPL, прародитель языка Си.
Представляет интерес проведенное в университете Карлсруэ эмпирическое сравнение языков Си, Си++, Java, Perl, Python, Rexx и Tcl по быстродействию и требованиям к памяти. Как отмечает Лутц Прехельт [3], «между средним временем выполнения программ на Java и сценариев нет существенной разницы. С вероятностью 80% сценарий будет выполняться в 1,29 раза дольше, а программа на Java по меньшей мере в 1,22 раза дольше, чем программа на Си или Си++». Что касается требований к оперативной памяти, то данные Прехельта тоже дают немалую пищу для размышлений: «Типичный сценарий занимает примерно вдвое больше памяти, чем программа на Си или Си++. Программы на Java занимают в три-четыре раза больше памяти, чем программы на Си и Си++».
Недостаточная надежность сценарных языков — тоже из разряда заблуждений. Так, яркий представитель сценарных языков 1990-х гг. — язык Python обладает средствами обработки исключений, построенными по образу и подобию аналогичного механизма в языке Modula-3. А ведь именно из него были заимствованы решения структурной обработки исключений (SEH), внедренные корпорацией Microsoft сначала в Си и Си++, а затем и в среду CLR (Common Language Runtime) платформы .NET.
Главная характерная черта для сценарных языков — динамическая природа, нередко позволяющая трактовать данные как программный код (и наоборот), а также простота освоения тех средств, которые буквально тут же дают видимый результат. Но это поверхностное наблюдение. Чтобы глубже разобраться в сути, необходимо выяснить, откуда пошли сценарные языки, для каких целей их создавали и что послужило катализаторами их развития.
все статьи |