Pull to refresh

Эволюция языка расширений: история Lua

Reading time 23 min
Views 27K
Original author: Roberto Ierusalimschy
от пер. Исходный материал датируется 2001 годом, так что некоторые моменты могут показаться забавными. Так же все отсылки на «сегодня», «в настоящий момент» и т.п. относятся к тому периоду.
Изложение ведется от лица автора, как и в оригинале.
Все ссылки добавлены мною.


Изложение организовано в хронологическом порядке. Мы начнем с наших экспериментов, легших в основу создания Lua в 1993, и пройдемся через восемь лет обсуждений, решений, работы и развлечений.

Введение


Есть старая шутка: «верблюд — это лошадь, разработанная комитетом». Среди разработчиков языков программирования эта шутка практически столь же популярна, как и легенда о разработанных комитетами языках. Легенда поддерживается такими языками, как Algol 68, PL/I и Ada, разработанными комитетами и не удовлетворившими ожиданий.
Однако, кроме комитетов, существует альтернативное объяснение частичного провала этих языков: все они были рождены большими. Каждый из них следовал нисходящему процессу проектирования, при котором язык полностью описывается еще до того, как программист может попробовать его в деле, да и даже до появления первого компилятора.

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

В этом документе описывается история языка программирования Lua. С момента своего появления в качестве языка для пары собственных специфичных «домашних» проектов, Lua ушел дальше наших самых оптимистичных ожиданий. По нашему мнению главные причины такого успеха лежат в нашем подходе к проектированию языка: сохранить сам язык простым и компактным, а его реализацию простой, компактной, быстрой, переносимой и свободной.

Lua был разработан (или, точнее, выдвинут) комитетом; пусть и маленьким — всего с тремя членами, — но комитетом. Задним числом мы понимаем, что первоначальная разработка языка небольшим комитетом положительно сказалась на языке. Мы добавляли новую возможность только по единодушному согласию, в противном же случае она откладывалась на будущее. Значительно проще добавлять новые возможности впоследствии, чем удалять их. Подобный процесс разработки позволял сохранять язык простым; и простота языка — это наш самый ценный вклад. Другие наиболее значимые качества Lua — скорость, компактность и переносимость, произошли от его простоты.

С первых версий у Lua были «настоящие» пользователи, а не только мы одни. Они вносили важный вклад в язык обсуждениями, жалобами, пользовательскими отчетами и вопросами. С другой стороны, наш маленький комитет играл важную роль: его структура давала нам достаточно инертности, чтобы прислушиваться к пользователям без следования всем их предложениям.

Начало


Наш первый опыт в TeCGraf (от пер. «группа технологий компьютерной графики Католического университета Рио-де-Жанейро в Бразилии»), связанный с разработкой своего ЯП, произошел в приложении для ввода данных. Инженерам PETROBRAS (бразильская нефтяная компания) по несколько раз в день требовалась подготовка файлов с исходными данными для симуляторов. Это был скучный и подверженный ошибкам процесс, так как программы симуляции требовали строго форматированные исходные файлы, состоявших обычно из простых колонок чисел без каких либо указаний какое число что означает. Конечно же, каждое число имело определенный смысл, понятный инженерам с первого взгляда на диаграмму конкретной симуляции. PETROBRAS обратились к TeCGraf за созданием графического фронтенда к подобным исходным данным. Числа могли вводиться интерактивно после простого клика в соответствующую часть диаграммы — и это было значительно более простой и наглядной задачей, нежели редактирование числовых колонок. Более того, это позволило добавить проверку и расчет зависимых величин по исходным данным, снижая тем самым объем данных требуемых от пользователя, а заодно увеличивая надежность всего процесса в целом.
Для упрощения разработки этого приложения в TeCGraf мы решили программировать все единообразно, разработав простой декларативный язык для описания всех задач по входным данным. Вот пример секции типичной программы на данном языке, названном нами DEL (data entry language):
:e      gasket            "gasket properties"
mat     s                  # material
m       f       0          # factor m
y       f       0          # settlement stress
t       i       1          # facing type

:p
gasket.m>30
gasket.m<3000
gasket.y>335.8
gasket.y<2576.8

Выражение :e объявляет сущность (названную в примере «gasket»), у которой есть несколько полей со значениями по умолчанию. Выражение :p накладывает некоторые ограничения на значения в gasket, реализуя таким образом проверку данных. В DEL есть также выражения для описания входных и выходных данных.
Сущность в DEL это, в общем-то, структура или запись в традиционных ЯП. Разница в том, что ее имя появляется также в графическом метафайле, содержащем диаграмму, с помощью которой инженеры вводят данные как было описано ранее.

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

Примерно в тоже время мы начали работать над другим проектом для PETROBRAS, названным PGM: настраиваемый генератор отчетов для литологических профилей. Как видно из названия, отчеты, создаваемые этой программой, должны были хорошо настраиваться: пользователь мог создавать и позиционировать треки, выбирать цвета, шрифты и тексты; у каждого трека могла быть сетка, имеющая свои настройки (логарифмическая/линейная, с вертикальными и горизонтальными отсечками, и т.д.); каждая кривая имела собственный автоматический масштаб; и многое другое.

Все эти настройки задавались конечными пользователями, обычно геологами или инженерами, а программе следовало работать на небольших машинах, таких как PC с MS-DOS. Мы посчитали, что лучший способ настройки приложения возможен через специализированный язык описаний, который мы назвали Sol: акроним от Простой Объектный Язык (Simple Object Language), а заодно это переводилось с португальского как «солнце».

Та как генератор отчетов использовал множество различных объектов со множеством атрибутов у каждого, мы решили не вводить эти объекты и атрибуты в язык. Вместо этого, язык позволял описывать типы. Основной задачей интерпретатора было прочитать описание, проверить корректность описания объектов и атрибутов, и передать информацию в основную программу. Для реализации взаимодействия между основной программой и интерпретатором последний был реализован как C библиотека, слинкованная с основной программой. Таким образом, основная программа имела полный доступ ко всей информации о конфигурации через API библиотеки. Более того, программа могла зарегистрировать callback-функцию на каждый описываемый тип данных, вызывавшуюся интерпретатором при создании объекта данного типа.

Вот типичный пример кода на Sol:
- объявляем тип 'track', с числовыми атрибутами 'x' и 'y',
- плюс бестиповый атрибут 'z'. для 'y' и 'z' заданы значения по умолчанию.
type @track { x:number,y:number= 23, z=0}

- объявляем тип 'line', с атрибутами 't' (трек)
- и 'z' (список чисел).
- у 't' есть значение по умолчанию - трек (тип 'track') с атрибутами x=8, y=23, и z=0.
type @line { t:@track=@track{x=8},z:number*}

- создаем объект 't1' типа 'track'
t1 = @track { y = 9, x = 10, z="hi!"}

- создаем линию (тип 'line') 'l' с t=@track{x=9, y=10}
- и z=[2,3,4] (список)
l = @line { t= @track{x=t1.y, y=t1.x}, z=[2,3,4] }

На синтаксис Sol оказали сильное влияние BiBTeX и UIL (User Interface Language), язык описания интерфейса пользователя в Motif.
В марте 1993 мы завершили первую реализацию языка Sol, но никогда не представляли ее. В середине 1993 мы поняли, что DEL и Sol можно объединить в единый более мощный язык. Программа визуализации литологических профилей вскоре потребовала поддержки процедурного программирования для создания более сложных слоев. С другой стороны, программам по вводу данных так же требовались описательные средства для программирования их пользовательского интерфейса.

Итак, мы решили, что нам нужен полноценный язык программирования с присваиваниями, управляющими структурами, процедурами, и всяким таким. Так же в языке нужны были средства описания данных по аналогии с Sol. Более того, так как множество потенциальных пользователей языка не были профессиональными программистами, то в языке следовало избегать сложного синтаксиса (и семантики). Наконец, реализация нового языка должна была быть хорошо портируемой.

Требование портируемости обернулось одним из основных достоинств: тем двум приложениям (от пер. судя по всему имеются ввиду DEL и Sol) следовало быть портируемыми, и таким же должен был быть язык. PETROBRAS, как госконтора, не мог выбирать конкретное аппаратное обеспечение, так как оно закупалось с очень жесткими ограничениями на трату государственных денег. Из-за этого в PETROBRAS было весьма разнообразное собрание компьютеров, на каждом из который должно было работать прграммное обеспечение, создаваемое в TeCGraf для PETROBRAS: сюда входили PC DOS, Windows (3.1 на тот момент), Macintosh и все возможные Unix.

В этот момент мы могли бы взять существующий язык, а не создавать еще один новый. Главными кандидатами были Tcl и, с большим отставанием, Forth и Perl. Perl — это не язык расширений. Плюс, в 1993 Tcl и Perl работали только на Unix платформах. Так же у всех трех языков сложный синтаксис. И ни один из них не имел хорошей поддержки описания данных. Так что мы начали работу над новым языком.

Вскоре мы поняли, что для наших целей в языке не нужно объявлять типы данных. Вместо этого мы могли использовать сам язык для написания функций проверки типов, основанных на базовых языковых возможностях рефлексии (таких как информация о типе во время выполнения (от пер. речь про RTTI)). Присваивание вида
t1 = @track {y = 9, x = 10, z="hi!"}

допустимое в Sol, так же допустимо в новом языке, но с другим смыслом: оно создает объект (в данном случае ассоциативную таблицу) с указанными полями, а затем вызывает функцию track для проверки объекта (и, иногда, для простановки значений по умолчанию).
Так как язык был измененной версией Sol («солнце»), друг в TeCGraf предложил имя Lua («луна» по португальски), и так родился язык Lua.

Lua наследовал от Sol синтаксис записей и конструирования списков, но объединил их реализацию с использованием ассоциативных таблиц: записи использовали строки (имена полей) в качестве индексов; списки же использовали целочисленные индексы. Кроме этих возможностей описания данных в Lua не было новых концепций, так как нам был нужен легкий язык общего применения. Итак, мы начали с небольшого набора управляющих структур, синтаксис которых был заимствован из Modula (while, if, repeat until). Из CLU мы взяли множественное присваивание и возврат нескольких значений как результата вызова функции (значительно более понятный подход, чем in-out параметры или передача по ссылке). Из C++ мы взяли идею локальности области видимости переменных в месте их объявления.

Одной из небольших (даже мелких) инноваций был синтаксис конкатенации строк. Так как язык позволял неявное приведение строк к числам, использование оператора "+" было бы двусмысленным, то мы добавили синтаксис ".." (две точки) для такой операции.

Споры вызвало использование ";" (точки с запятой). Мы считали, что требование использования точки с запятой несколько запутает инженеров, знающих FORTRAN, с другой же стороны не использование ее запутает тех, кто знает C или Pascal. В конце концов, мы пришли к решению опциональности использования точки с запятой (типичное решение комитета).

Первоначально язык Lua имел семь типов данных: числа (хранящиеся в формате с плавающей точкой), строки, (ассоциативные) таблицы, nil (тип данных с уникальным значением так же названным nil), userdata (простой C'шный указатель для представления структур данных C внутри Lua), Lua функции и C функции. (Спустя восемь лет эволюции языка единственным изменением в этом списке стала лишь унификация Lua и С функций в единый тип). Для сохранения компактности языка мы не включили булевый тип данных. По аналогии с Lisp, nil приводится к false, тогда как все остальные значения приводятся к true. Это одна из немногих экономий о которой мы теперь иногда жалеем.

Lua также перенял из Sol подход к реализации в качестве библиотеки. Реализация следовала принципу, поддерживаемому сейчас Экстремальным Программированием: «самая простая реализация, которая может работать». Мы использовали lex для лексического сканера и yacc для синтаксического парсера. Парсер переводил программу в байткод, который затем выполнялся простым стековым интерпретатором. У языка была очень маленькая стандартная библиотека и было очень легко добавлять новые функции в C.

Несмотря на простую реализацию – или, возможно, из-за нее – Lua превзошел наши ожидания. Оба прокта (PGM и ED) успешно использовали Lua (а PGM все еще используется). В конце концов и остальные проекты в TeCGraf начали использовать Lua.

Первые годы (1994–1996)


Новые пользователи создают новые запросы. Не удивительно, что одним из первых запросов было увеличение производительности Lua. Применение Lua для описания данных поставили нетипичную проблему для обычного скриптового языка.
Вскоре после того, как мы начали использовать Lua, мы заметили его потенциал для использования в качестве языка для графических метафайлов. Возможности описания данных в Lua позволили использовать его как графический формат. В сравнении в другими программируемыми метафайлами использование Lua давало все преимущества полноценного процедурного языка. Формат VRML, к примеру, использует Javascript для моделирования процедурных объектов, что приводит к неоднородности (и, следовательно, неясности) кода. С Lua объединение процедурных объектов в описании сцены происходит естественно. Фрагменты процедурного кода могут быть объединены с декларативными выражениями для моделирования сложных объектов при сохранении общей ясности.

Программа по вводу данных (ED) была первой, использовавшей Lua для своих графических метафайлов. Было обычным делом иметь диаграммы с тысячами частей, описываемых тысячами элементов в Lua в файле на сотни килобайт. Это значило, что с точки зрения языков программирования Lua справляется с огромными программами и выражениями. А то, что Lua компилировал такие программы «на лету» (как «just-in-time» компилятор), означало и так весьма высокую скорость самого Lua компилятора. Первой жертвой погони за производительснотью стал lex. Замена сканера, генерируемого lex, на самописный код практически удвоило скорость Lua компилятора.
Мы также создали новые опкоды для конструкторов. Исходный код для конструктора списков выглядел так:
@[30, 40, 50]

что превращалось в подобный байткод:
CREATETABLE
PUSHNUMBER 1                 # индекс
PUSHNUMBER 30                # значение
SETTABLE
PUSHNUMBER 2                 # индекс
PUSHNUMBER 40                # значение
SETTABLE
PUSHNUMBER 3                 # индекс
PUSHNUMBER 50                # значение
SETTABLE

С новой схемой код стал выглядеть так:
CREATETABLE
PUSHNUMBER 30                # значение
PUSHNUMBER 40                # значение
PUSHNUMBER 50                # значение
SETTABLE 1 3                 # задание элементов с индексами от 1 до 3

Для длинных конструкторов не было возможности сложить все их элементы в стек перед сохранением. Из-за этого генератор кода время от времени выдавал опкод SETTABLE для сброса стека.
(С тех пор мы всегда пытались улучшить время компиляции. Сейчас Lua компилирует программу с 30000 присваиваний в шесть раз быстрее Perl, и в восемь раз быстрее Python).

Мы выпустили новую версию Lua с этими оптимизациями в июле 1994 под именем Lua 1.1. Версия была доступна для скачивания по ftp. Предыдущая версия Lua 1.0 никогда не была доступна публично. Спустя некоторое время мы также выпустили первую документацию с описанием Lua.

У Lua 1.1 была ограниченная пользовательская лицензия. Язык можно было свободно использовать в академических целях, но не для коммерческого использования. (Несмотря на лицензию, сам язык всегда был с открытым исходным кодом). Но такая лицензия не работала. Большинство конкурентов, таких как Perl и Tcl, были свободны. Более того, ограничения коммерческого использования мешали даже академическому использованию, так как некоторые академические проекты планировали в последствии выйти на рынок. Так что выпуск следующей версии языка, Lua 2.1, был свободным.

Lua версии 2


Lua 2.1 (выпущенный в 1995), принес много важных изменений. Одно из них было не в самом языке, а в процессе разработки: мы считали, что стоит всегда пытаться улучшить язык, пусть даже ценой небольшой обратной несовместимости.
В версии 2.1 мы внесли много несовместимостей с версией 1.1 (но предоставили инструменты для помощи в переносе кода). Мы выкинули синтаксис @ из конструкторов таблиц и унифицировали использование фигурных скобок и для записей и для списков. Выбрасывание @ было тривиальным изменением, но изменило восприятие языка, не только его внешность.

Что более важно, мы упростили семанику конструкторов. В Lua 1.1 конструкция
@track{x=1, y=10}
имела особый смысл. В Lua 2.1 конструкция
track{x=1, y=10}
является синтаксическим сахаром для
track({x=1, y=10})
, то есть она создает новую таблицу и передает ее единственным параметром в функцию track.

С самого начала мы разрабатывали Lua как язык расширений, в связи с чем программы на C могли регистрировать свои собственные функции, прозрачно вызываемые из Lua. С таким подходом было легко расширять Lua предметно-ориентированными примитивами, что позволяло конечному пользователю адаптировать язык под конкретные задачи.

В версии 2.1 мы ввели понятие fallback'ов: определенные пользователем функции, которые вызываются Lua в случае неопределенных ситуаций. (от пер. Этот подход является своеобразным надмножеством перегрузки операторов. Принцип похожий, но возможностей больше). Lua становится языком, который можно расширять двумя путями: расширение набора «примитивных» функций и расширением их семантики через fallback'и. Вот почему сейчас мы называем Lua расширяемым языком расширений.

Мы объявили fallback'и для арифметики, сравнения операторов, конкатенации строк, доступа к таблицам и т.д. (от пер. подробнее тут и несколько наперед тут). После задания пользователем, подобная функция вызывается всякий раз, когда операнды этой операции не подходят по своим типам. К примеру, при сложении двух значений, одно из которых не является числом, вызывается fallback, а результат его вызова используется в качестве итоговой суммы.

Особый интерес представляет собой (и является главной причиной появления fallback'ов) операция доступа к таблице: если при выполнении x=a[i] значением a[i] является nil (от пер. т.е. таблица a не содержит поля i), то вызывается (если задана) fallback функция, чей результат используется как значение для a[i]. Этот простой новый функционал позволил программистам реализовать различные семантики доступа к таблицам. В частности, можно реализовать некоторый вид наследования через делегирование:
function Index (a,i)
    if i == "parent" then       -- чтобы избежать цикла
        return nil
    end
    local p = a.parent
    if type(p) == "table" then
        return p[i]             -- возможнен повторный вызов функции Index
    else
        return nil
    end
end

setfallback("index", Index)

Этот код поднимается по цепочке «родителей», пока не найдет нужного поля или не достигнет конца. С этим кодом следующий пример выведет «red» даже если у b нет поля с цветом.
a = Window{x=100, y=200, color="red"}
b = Window{x=300, y=400, parent=a}
print(b.color)

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

Еще один fallback будет вызван для выражения a[i] в случае, если a вообще не является таблицей. Это fallback «gettable», срабатывающий для получения значения a[i] в ситуации вида x=a[i], и fallback «settable», срабатывающий при записи в a[i] в ситуации вида a[i]=x.

Есть много возможностей использования этих табличных fallback'ов. Межязыковое взаимодействие это очень мощная возможность: когда a хранит значение типа userdata (указатель на что-либо в C коде), fallback реализует прозрачный доступ к значениям внутри структур данных основной программы.

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

Мета-механизм fallback'ов позволяет Lua поддерживать ООП в контексте некоторых реализуемых видов наследования и перегрузки операторов. Мы даже добавили немного синтаксического сахара для описания и использования «методов»: функции могут объявляться в формате a:f(x,y,z), и в этом случае при вызове добавляется скрытый параметр self, делающий вызов a:f(10,20,30) эквивалентным a.f(a,10,20,30).

В мае 1996 мы выпустили Lua 2.4. Основным нововведением этой новой версии был внешний компилятор, названный luac. Эта программа компилировала Lua код и сохраняла байткод и таблицы строк в бинарный файл. Формат такого файла был выбран для простой загрузки и переносимости между различными платформами. С luac программы могли избежать парсинга и кодогенерации во время запуска, что было накладно, особенно для больших статичных программ, таких как графические метафайлы.

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

Кроме ускорения загрузки luac позволяет так же выполнять проверку синтаксиса при компиляции и защиту исходного кода от изменений пользователем. Однако предварительная компиляция не ускоряет выполнение, так как Lua и так всегда предварительно компилирует исходный код перед выполнением.

luac реализован в «режиме клиента», то есть он использует модули, реализующие Lua как с простого клиента, даже не смотря на то, что используются приватные заголовочные файлы для доступа ко внутренним структурам данных, требующих сохранения. Одним преимуществом такой политики является разделение реализации ядра Lua на четко разделенные модули. Благодаря этому, сейчас легко удалить модули парсинга (лексер, парсер и кодогенератор), занимающие 40% кода ядра Lua 4.0, оставив лишь крошечный модуль загрузки предварительно скомпилированных фрагментов кода. Это может быть полезно для очень маленьких реализация Lua для встраивания в небольшие устройства вроде мобильных телефонов или роботов (к примеру, Crazy Ivan, робот побеждавший в RoboCup в 2000 и 2001 в Германии, имел «мозги» реализованные на Lua).

Представление миру (1996–2000)


В июне 1996 мы опубликовали академическую статью про Lua в Software: Practice & Experience (от пер. по ссылке скачивание за денежку. нашел в другом месте судя по всему тот же самый материал и конвертнул в pdf, если кому интересно). В декабре 1996 журнал Dr. Dobb's опубликовал статью про Lua. Данные публикации, нацеленные на разные сообщества, привели к международной известности Lua.

Вскоре после статьи в Dr. Dobb's мы получили множество писем про Lua. Одно из первых писем было следующим:
From: Bret Mogilefsky <mogul@lucasarts.com>
To: "'lua@icad.puc-rio.br'" <lua@icad.puc-rio.br>
Subject: LUA rocks!  Question, too.
Date: Thu, 9 Jan 1997 13:21:41 -0800

Привет...

Прочитав статью Dr. Dobbs про Lua, я с нетерпением проверил его,
и результат превысил все мои возможные ожидания!
Его элегантность и простота поразили меня. Мои поздравления за
разработку столь хорошо продуманного языка.

Обо мне: я работаю над адвенчурой в LucasArts Entertainment Co.
и хочу попробовать заменить наш старый скриптовый язык SCUMM на Lua.

[...]

Оказалось, что Bret Mogilefsky был ведущим программистом Grim Fandango, основной адвенчуры LucasArts в 1997 году. В другом письме он пишет нам, что «ОГРОМНАЯ часть игры написана на Lua» (акцент авторский). Это первое использование Lua в игре привлекло к языку внимание множества игровых разработчиков во всем мире. Спустя некоторое время Lua все чаще стал появляться в игровых группах новостей вроде rec.games.programmer и comp.ai.games.
Вследствие небольшого размера, хорошей производительности, переносимости и простоты интеграции Lua приобрел большую популярность для расширения функционала игр. В наше время некоторые игровые компании применяют Lua (к примеру LucasArts, BioWare, Slingshot Game Technology и Loewen Entertainment) и знание Lua является конкурентным преимуществом при поиске работы в игровой индустрии. По нашей оценке половина пользователей Lua вовлечены в разработку игр, но сложно посчитать точнее из-за большой секретности в игровой индустрии. К примеру, хоть Bret Mogilefsky и адаптировал Lua для Grim Fandango, но подробности не доступны публике.

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

Не так давно, в 2000, LucasArts выпустили другую игру, использующую Lua, Escape from Monkey Island, которая была четвертой в серии адвенчур Monkey Island. В этой игре в дань уважения Lua авторы переименовали внутриигровой бар из SCUMM (ранее используемый разработчиками язык программирования) в Бар Lua.

Помимо широкого использования в компьютерных играх (к примеру, Grim Fandango, Baldur's Gate, MDK2, Escape from Monkey Island) Lua используется во многих других сферах по всему миру.

Одним из первых мест применений Lua вне PUC-Rio (от пер. тот самый католический университет Рио-де-Жанейро, подразделением которого является TeCGraf) была Смитсоновская астрофизическая обсерватория. Они разработали обобщенную программу апертуры для имитации воздействия потока фотонов на физические препятствия. Данная программа была частью усилий по продвижению программы AXAF (объект продвинутой рентгеновской астрофизики) — третьей из четырех Великий Космических Обсерваторий NASA.

Performance Technologies использовали Lua для реализации интерфейса командной строки для CPC4400 — ethernet свич с возможностью горячей замены. Используя Lua в качестве скриптового языка для CPC4400, пользователь может связывать происходящие события (такие как статус соединения, изменения топологии, тревоги RMON) со скриптами на Lua.

Tollgrade Communications использовали Lua в их продукте нового поколения для тестирования телефонной сети DigiTest. Lua использовался для пользовательского интерфейса, автоматизированных тестовых скриптов и результатов анализа.

Lua так же используется в InCor Heart Institute (Instituto do Coração, São Paulo) в Бразилии, в CEPEL (иследовательский центр государственной электроэнергетической компании ELETROBRAS) так же в Бразилии, в Weierstrass Institute в Берлине, в берлинском техническом университете, и во множестве других мест.

В 1998 Cameron Laird и Kathryn Soraiz в их колонке про скриптовые языки в журнале SunWorld подсчитали, что «в мире где-то около нескольких сотен тысяч Lua программистов». По их мнению это «небольшое число пользователей», но для нас это явный знак к росту популярности языка.

Lua версии 3


Lua 3.0 (июль 1997) заменила fallback'и на более мощный концепт теговых методов. Fallback'и были глобальными по своей природе: пользовательские функции вызывались всякий раз при возникновении события, при этом можно было задать лишь одну функцию на конкретное событие. Это усложняло комбинирование Lua модулей, которые, к примеру, использовали различный подход к реализации наследования. Хоть и была возможность реализации цепочек вызова fallback функций, этот подход был медленным и склонным к ошибкам, и вряд ли кто-то стал бы им пользоваться.
С версии Lua 3.0 программист мог создавать теги и связывать с ними таблицы и userdata. Методы тегов — это, по существу, все теже fallback'и, выбираемые согласно тегу оператора. С тегами и методами тегов различные таблицы (и userdata) могли использовать различные fallback функции для своих операций.

Концепция тегов предлагается Lua для пользовательских типов. Другими словами, тег это просто число, представляющее новый тип. Когда вы ассоциируете таблицу с конкретным тегом, на самом деле вы объявляете новый тип для этой таблицы: тип (или тег) указывает как он реализует эти операторы.

При первоначальном введении fallback'ов, большинство из них описывало поведение Lua в ошибочных ситуациях, таких как обращение к индексу не у таблицы или попытка вызова не функции. Так что мы считали, что fallback'и — это механизм обработки исключительных ситуаций. С введением пользовательских тегов, fallback'и (называемые теперь методами тегов) стали основным механизмом для описания поведения новых типов, даже не смотря на то, что мы все еще используем их для расширения поведения базовых типов.

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

В Lua 3.0 так же появилась поддержка условной компиляции в формате похожего на C препроцессора. Как и любая языковая возможность, добавить эту было очень легко (хоть это и усложнило лексер), и вскоре программисты начали ее использовать (программисты используют любые возможности языка). При появлении нового функционала, сразу растет спрос на его дальнейшее развитие. Одним из самых частых запросов было добавление макросов, но продолжительное обсуждение так и не вылилось в четкое предложение ни в списке рассылок, ни между нами. Каждое из предложений требовало огромных изменений в лексере и парсере, не показывая при этом явной выгоды. Так что препроцессор оставался статичным с Lua 3.0 и до версии 3.2 (в течение двух лет).

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

Lua версии 4


До версии 3.2 только одно «состояние» Lua-машины могло быть активно в один момент. У нас было API для смены состояния, но оно было несколько неудобно для использования. Для упрощения при разработке API мы не включали явный параметр состояния в функции — было лишь одно единственное глобальное состояние. Теперь понятно, что это было ошибкой. К моменту выхода Lua 3.2 стало ясно, что многие приложения будут проще, если они смогут удобно работать с несколькими Lua состояниями. Например, мы делали специальную версию Lua 3.2, включенную в CGILua — расширение браузеров для отдачи динамических страниц (от пер. все-таки имеется ввиду серверная сторона) и CGI программирования на Lua. Ранее, LucasArts делала нечто подобное для Lua 3.1.
При обсуждении наших планов по Lua 3.3 высочайший приоритет был у API с явными состояниями. Однако, это поднимало вопрос обратной совместимости. В конце концов, из-за несовместимостей мы решили переписать API для следующей версии, которой стала Lua 4.0. Теперь API не только содержит явные состояния, но оно так же стало проще и эффективней. Мы боялись, что переход на новое API будет не самым гладким, так как это был первый случай серьезных изменений в API со времен Lua 1.1. Мы получили несколько жалоб в списке рассылок, но в целом изменение вообще не оказалось травмирующим. Многие Lua программисты не взаимодействовали непосредственно с API, многие использовали его только через автоматические инструменты, а многие считали, что преимущества превышают цену перехода.

Мы выпустили Lua 4.0 в ноябре 2000. Кроме нового API, эта версия принесла много других небольших улучшений, например цикл.

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

С версии 1.1 конструкция for была в пожеланиях большинства пользователей Lua. Основной причиной недовольства была забывчивость людей выполнять инкремент в конце итерации цикла while, что приводило к зацикливанию.

Мы довольно скоро согласились с этим. Но, хоть мы все и были согласны с необходимостью цикла for, мы не могли согласиться с конкретными синтаксическими конструкциями для него. Мы считали конструкцию в стиле Pascal (или Modula) слишком ограничивающей, так как она не предполагала итерации по элементам таблицы или по строкам файла. Более того, перевод идентификатора to в список зарезервированных слов привел бы к недопустимой обратной несовместимости. С другой стороны, цикл for в стиле C не подходил к Lua.

С введением замыкаций и анонимных функций в версии 3.1 (июль 1998) мы решили использовать для итераций функции высшего порядка. (На самом деле, необходимость в for была одной из основных причин введения анонимных функций в Lua). Lua 3.1 вышла с двумя предопределенными функциями для итерирования:
foreach(table, f)
foreachi(table, f)

Функция foreach применяет f для всех пар ключ-значение, выбранных в произвольном порядке из заданной таблицы. Функция foreachi похожа, но рассматривает таблицу как список (или массив): она перебирает только элементы с числовыми ключами в порядке возрастания значений ключей. Хоть мы и предоставили только эти два способа обхода, было легко создать новые итераторы.
Но хоть и просто было создавать новые, но за более чем два года практически никто их не делал. Первой причиной был дискомфорт для многих программистов использовать анонимные функции и функции высших порядков в процедурном языке. А второй и, на наш взгляд, значительно более важной причиной было отсутствие необходимости для большинства разработчиков в других итераторах. Это означает, что многие годы мы пытались добиться того, что не нужно реальному пользователю. С пониманием этого, мы быстро разработали два формата цикла for: для числовой итерации и для обхода таблиц.

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

Заключение


Сейчас у Lua устоявшаяся пользовательская база. Активен список рассылки почти на 500 человек из более чем 30 различных стран мира. На сайте (www.lua.org) примерно 500 посетителей в день из 50 стран. Язык используют в спектре от адвенчур до веб-сверверов и до тестирования телефонной сети для Ethernet свичей.
Несколько ftp серверов предлагают исходные коды Lua, а также несколько других сайтов распространяют версии для конкретных платформ, таких как DLL для Windows, SIS для EPOC, RPM для Linux, бинарники для RISC OS, и т.д. Более того, некоторые журналы распространяют Lua на добавочных CD дисках (к примеру Dr. Dobb's, Linux Magazine France и японский C Magazine).

Основной вклад Lua как языка заключается в предоставлении мета-механизмов вместо функциональности. Успех Lua как продукта пришел из его простоты, небольшого размера и портируемости реализации, позволивших использовать Lua на множестве различных платформ, включая небольшие устройства вроде palm'ов, КПК, специализированных плат и роботов. Cameron Laird и Kathryn Soraiz предсказали в 1998, что «неизбежный взрыв повсеместного использования встраиваемых устройств (компьютеров в вашей машине, ванной, и кухонной технике) может пойти лишь на пользу Lua». Тогда мы не обратили на это особого внимания, но они были правы.

Также Lua внес вклад в академическую деятельность благодаря нескольким тезисам и публикациям как о самом языке, так и о его технологическом использовании.

Успех имеет свою цену. В процессе эволюции языка обратная совместимость все сильнее сдерживала инновации. Тем не менее, мы не позволяли совместимости остановить прогресс — это лишь один из многих ингредиентов (пусть даже и сильный) в алхимии дизайна языков.

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

Благодарности


Lua бы никогда не было без помощи многих людей. Каждый в TeCGraf принимал какое-либо участие: использование языка, его обсуждение, распространение за пределы TeCGraf. Особая благодарность Marcelo Gattass, главе TeCGraf, кто всегда поддерживал нас и давал нам полную свободу касательно языка и его реализации. Lua был реально первым продуктом TeCGraf, публично доступным в интернете еще даже до подъема популярности.
Без пользователей Lua был бы просто еще одним языком. Пользователи и их цели — это максимальная проверка для языка. От них мы получали отчеты о багах, недостатках дизайна, новых путях восприятия реальности. Особая благодарность членам списка рассылки за их обсуждения, предложения и, в основном, за терпение к нашему время от времени авторитарному подходу.

от пер. Если тема будет интересна, то могу собрать информации по более новым версиям Lua, а так же про реализацию luajit.
Спасибо, что дочитали :)
Tags:
Hubs:
+39
Comments 8
Comments Comments 8

Articles