Пол Грэм: Lisp для веб-приложений

Эссе Пола Грэма Lisp for Web-Based Applications, 2001 год.

Пол Грэм (отрывок беседы в BBN Labs в г. Кембридж, штат Массачусетс, апрель 2001)

Оглавление

  1. Любой язык, какой хотите
  2. Поэтапное развитие
  3. Простая интерактивная среда программирования
  4. Макросы для HTML
  5. Встроенные языки
  6. Эмуляция подпрограмм с использованием замыканий

1. Любой язык, который вы хотите

Одна из причин использовать Lisp в написании веб-приложений — вы действительно можете использовать его. Когда вы пишете ПО, предназначенное для работы только на ваших серверах, вы можете использовать любой язык, какой захотите.

Долгое время у программистов не было свободы выбора языка, используемого для написания прикладных программ. До недавнего времени написание таких программ означало написание программного обеспечения для настольных ПК. В ПО для дескопов была тенденция писать приложения на том же языке, на котором была написана операционная система. 10 лет назад все приложения для практических целях писались на языке С.

Ситуация изменилась с появлением веб-приложений. Вы управляете серверами и можете писать своё ПО на любом языке. Вы принимаете как должное, что у вас есть исходный код как операционной системы, так и компиляторов. Если обнаружится любая проблема между языком и ОС, вы можете исправить её самостоятельно.

Однако, такая свобода — обоюдоострый меч. Более широкий выбор означает, что теперь вам придётся думать, какой выбор сделать. В прошлом было проще. Если вы руководили проектом по разработке ПО, и кто-то назойливо предлагал писать его на другом языке вместо того, который вы обычно используете — вы могли просто сказать ему, что это непрактично, и и на этом закончить разговор.

Теперь, с приходом серверных приложений, всё изменилось. Теперь вы зависите от рынка в вопросе выбора языка. Если вы пытаетесь притворяться, что ничего не изменилось и продолжаете использовать С и С++, как большинство ваших конкурентов — вы просто готовите свой провал. Ваш ужин достанется какому-нибудь маленькому стартапу, использующему более мощный язык.

2. Поэтапное развитие

Существует определенный стиль разработки ПО, связанный с Lisp. Одна из его традиций — поэтапное развитие: вы начинаете с предельно быстрого написания программы, которая практически ничего не делает. Затем вы постепенно добавляете новые функции, но на каждом этапе у вас есть работающий код.

Я думаю, что таким образом вы получаете лучшее и написанное быстрее ПО. В языке Lisp все сделано и настроено именно для такого стиля разработки, так как Lisp-программисты прокладывали эту дорогу, как минимум, 30 лет.

Viaweb , должно быть, один из самых экстремальных случаев поэтапного развития. Всё началось с 120-строчной программы для генерации веб-сайтов, которую я использовал в качестве примера в книге, которую закончил прямо перед тем, как мы запустили Vieweb. Редактор Viaweb, конечный вариант которого состоял из 25.000 строк кода, вырос постепенно из этой программы.

Я ни разу не садился, чтобы переписать весь код целиком. Я не думаю, что мой код переставал работать больше, чем на 1-2 дня. Весь процесс разработки был одной длинной серией постепенных изменений.

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

3. Простая интерактивная среда программирования

Система Top-level в Lisp (механизм для интерактивной оценки выражений) — отличное подспорье в быстрой разработке ПО. Но самое большое преимущество для нас состояло, пожалуй, в поиске багов. Как я уже говорил, при работе с веб-приложениями данные пользователей хранятся на ваших серверах, и часто могут вызывать баги.

Когда кто-то из службы поддержки клиентов приходит ко мне с отчётом о баге в редакторе, я загружаю код в интерпретатор Lisp и захожу в аккаунт пользователя. При воспроизведении ошибки я получаю точное место проблемы, говорящее мне, что именно пошло не так. Часто я мог сразу пофиксить код и выпустить патч. Под “сразу” я имею в виду — пока пользователь ещё на телефоне.

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

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

4. Макросы для HTML

Макросы Lisp стали еще одной большой победой для нас. В редакторе Viaweb мы использовали их очень активно. Его можно довольно точно описать, как один большой макрос. И этот факт даст вам представление, как сильно мы зависели от Lisp¸ потому что ни в каком другом языке нет макросов в том понимании, в каком их даёт Lisp.

Например, мы использовали макросы для генерации HTML. Между макросами и HTML существует естественная связь, потому что HTML рекурсивен, как и Lisp, и обладает префиксной нотацией, как Lisp. Поэтому у нас был макрос, вызываемый внутри макроса, вызывающего генерацию самого сложного HTML, и это всё ещё была вполне управляемая система.

5. Встроенные языки

Ещё одна одно ценное свойство макросов — встроенный язык для описания страниц — RTML. (Мы придумали много разных названий, соответствующих функции RTML, но на самом деле я назвал его в честь Роберта Морриса (Robert Morris) , сооснователя Viaweb, чьё имя пользователя было Rtm). (В 2005 Роберт стал соинвестором Y Combinator совместно с Полом Грэмом, Джессикой Ливингстон и Трэвором Блэквеллом).

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

Пользователи могли писать свои собственные RTML-шаблоны, чтобы описать, как должны выглядеть их страницы. У нас был редактор структуры для манипуляций с этими шаблонами, сильно похожий на редактор, который был у них в Interlisp.

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

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

Мы разработали RTML так, чтобы исключить возникновение ошибок в процессе выполнения: каждая RTML-программа выдавала некую веб-страницу, которую вы могли отлаживать до тех пор, пока он не она не выдавалась в том виде, который вам нужен.

Изначально мы ожидали, что нашими пользователями станут веб-консультанты, и что будут активно использовать RTML. Мы предоставили несколько стандартных шаблонов страниц разделов, страниц товаров и т.д. — идея состояла в том, что пользователь может брать любые из этих страниц и модифицировать под то, что им нужно.

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

Таким образом, мы практически не привлекли внимание веб-консультантов. Вместо них к нам потянулись конечные пользователи — сами торговцы. Им понравилась идея контроля над своими веб-сайтами. И этот тип пользователя не хотел заниматься программированием ни в каком виде. Они просто использовали стандартные шаблоны.

Таким образом, роль RTML не закончилась на том, что он стал главным интерфейсом программы. В конечном итоге, он сыграл 2 роли. Прежде всего, он стал выходом для искушённых пользователей, которые хотели чего-то, что не могли дать наши шаблоны.

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

Но в реальности только 1 из 100 пользователей писал свои собственные шаблоны. И это привело ко второй важной роли RTML. Глядя на изменения, которые делали пользователи в стандартных шаблонах, мы понимали, что в них нужно добавить.

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

Третьим, и самым большим достоинством использования RTML было преимущество, которое мы получали от его использования сами. Даже если бы мы были единственными людьми, которые его используют, он бы очень помог в написании нашего софта именно таким образом.

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

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

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

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

RTML был очень "Lisp-ориентированным". Во-первых, это макрос Lisp. Онлайн-редактор за кулисами незаметно манипулировал s-выражениями. И когда люди запускали шаблоны, они компилировались в функции на Lisp, вызывая прекомпиляцию "на лету".

RTML сильно зависит от ключевых параметров, которые я до этого времени считал одной из самых сомнительных особенностей Common Lisp . Из-за способа, которым выпускается ПО веб-приложений, вы должны разрабатывать его так, чтобы его было легко изменять. И сам по себе RTML должен быть так же легко изменяем, как и прочие части ПО.

Большинство операторов в RTML были разработаны так, чтобы принимать ключевые параметры — и как это оказалось удобно! Если я хотел добавить еще один вариант работы оператора, я мог просто добавить новый ключевой параметр, и все существующие шаблоны продолжали работать.

Несколько RTML-операторов не принимали ключевые параметры, потому что я не думал, что мне когда-либо придётся их менять. Позже я сильно об этом жалел (относительно каждого из этих операторов). Если бы я мог начать сначала, я бы сделал так, чтобы каждый RTML оператор принимал ключевые параметры.

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

6. Эмуляция подпрограмм с использованием замыканий

Одной из проблем использования веб-страниц в качестве пользовательского интерфейса — это то что пользовательский интерфейс является неотъемлемой частью веб сеанса. Мы обошли это препятствие, используя лексическое замыкание (lexical closure) для эмуляции работы подпрограмм.

Если вы знаете о продолжениях (continuations), то один из способов объяснить то, что мы сделали — сказать, что мы писали свое ПО в стиле continuation-passing style, или CPS. (В функциональном программировании CPS называется стиль программирования, при котором порядок выполнения передается в форме продолжения).

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

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

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

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

Что происходит, когда какой-то объект имеет свойство – цвет? Если вы используете обычные cgi-скрипты, где всё должно происходить в одной форме, то после нажатия кнопки «Обновить» внизу вас ожидают трудности. Вы можете использовать текстовое поле и заставить пользователя вводить RGB-номер, но конечным пользователям это не понравится.

У вас может быть палитра возможных цветов, но тогда вам придется ограничить число возможных цветов. Или вы можете использовать только стандартную веб-палитру, но тогда вам понадобится меню из 256 элементов с едва отличающимися именами.

В Viaweb мы смогли показывать цвет в качестве образца, представляющего текущее значение, которое можно отредактировать, нажав кнопку «Изменить». Если пользователь нажимал кнопку «Изменить», он попадал на страницу с палитрой цветов, из которых мог выбирать. После выбора цвета, он возвращался на страницу редактирования свойств объекта, где цвет уже был изменен.

Именно это я и имел ввиду , говоря об “эмуляции работы подпрограмм” (simulating subroutine-like behavior). Софт ведёт себя так, как будто только что было возвращено выбранное значение цвета. Конечно, на самом деле этого не происходило — просто был сделан новый вызов cgi, который выглядел как возврат обратно в стек.

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

История языка RTML

Далее — перевод отрывка описания истории Lisp из Википедии:

RTML появился в компании Viaweb, основанной в 1995 году Полом Грэмом и Робертом Моррисом, как язык для написания и модификации шаблонов их екоммерс-платформы. Одна из версий расшифровки названия — в честь Роберта ("Robert T. Morris Language").

Редактор RTML предлагался в качестве опции клиентам (как правило, малому бизнесу), которые хотели настроить свои интернет-магазины более гибко, чем это позволяли предустановленные шаблоны (которые тоже были написаны на RTML).

В 1998 году поисковик Yahoo! купил Viaweb за $49.6 (почти 50 миллионов долларов) и переименовал её в Yahoo! Store. Позже Yahoo! представила Yahoo! Site — CMS на основе RTML на хостинге без корзины.

В 2003 Yahoo! переименовал Yahoo! Store в Yahoo! Merchant Solutions (часть Yahoo! Small Business), и сразу начала предлагать новым пользователям на выбор более стандартную среду PHP/MySQL хостинга вместо редактора магазинов на RTML.

По состоянию на 2006 год, многие новые сайты на Yahoo! Merchant Solutions и Yahoo! Stores продолжают создаваться на Store Editor и RTML.

Язык

Хотя в документации Yahoo! это не упоминается, язык RTML был построен на основе Lisp.

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

Программист выбирает ключевое слово кликом на него и редактирует его атрибуты. Блоки кода можно помещать и брать из буфера обмена, используя стек. Редактор автоматически поддерживает структуру s-выражений кода и представляет её визуально в веб-интерфейсе с помощью индентации вместо скобок Лиспа.

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

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

Варианты расшифровки аббревиатуры

В документации Yahoo!'s говорится, что RTML — это акроним названия "Real Time Markup Language" (язык разметки в реальном времени), но Пол Грэм признался, что "изначально он был назван в честь второго основателя Роберта, чьи инициалы — RTM.