Помню, как в студенческие годы я постоянно терял баллы на заданиях по программированию. Преподавателю просто не нравилось, что мой исходный код мог занимать больше 80 столбцов, а работать с ним в Emacs оказывалось неудобно. Мне также приходилось вступать в споры о том, как лучше делать отступы — при помощи пробелов или табуляции, а также о том, следует ли переносить фигурную скобку на новую строку. Как ни странно, со времени появления первых высокоуровневых языков — LISP и FORTRAN — мы до сих пор пишем исходный код в текстовых файлах с кодировкой ASCII (или UTF-8). Cо времен PDP-11 в этом отношении программирование почти не изменилось.
Мне как разработчику компиляторов это кажется несколько странным. Ведь работа любого компилятора начинается с синтаксического анализа вашего исходного кода и преобразования его в так называемое «абстрактное синтаксическое дерево», сокращенно — AST. Этот этап необходим компилятору, чтобы он мог понять смысл вашей программы. Но дело не только в компиляторах. Статические анализаторы, генераторы документации или lint нужны именно по этой причине: программа не может напрямую оперировать текстовыми исходниками.
Конечно, работать с текстовыми исходниками удобно (как с точки зрения простоты, так и в контексте обеспечения взаимодействий). Но я считаю, что было бы целесообразно проектировать программы таким образом, чтобы исходный код был представлен и редактировался уже в виде иерархической структуры данных. Я имею в виду, что новые языки программирования не должны так сильно зависеть от текстовых файлов. Полагаю, что работа с потоками символов накладывает ненужные ограничения на дизайн языков, снижает их выразительность, а в долгосрочной перспективе пагубно сказывается на инструментальной поддержке языка и на организации взаимодействий.
Если мы проектируем язык, который оперирует линейными потоками символов, нам приходится сталкиваться с глупыми проблемами, в частности:
- разрабатывать недвусмысленную грамматику;
- пользоваться разделительными символами и операторами, в основе которых лежит не формальная нотация, а кодировка ASCII;
- ограничивать длину ключевых слов для экономии экранного пространства;
- справляться с неоднозначностью, возникающей при поиске, связанной с названиями символов, конфликтами имен, сокрытием переменных;
- работать с моноширинными шрифтами и несогласованными вариантами компоновки исходного кода;
- различать значимые и незначимые пробелы;
- много работать с документацией и аннотировать исходный код специальными комментариями.
Говоря о выразительности, вспомним одну из самых замечательных идей LISP: поговорим о макроcах.
В языке C тоже есть макросы, но они основаны на подстановке текста. Считается общепризнанным, что система макросов в C сделана плохо и чревата множеством ошибок, зачастую прибегать к таким макросам не рекомендуется. Напротив, в основе макросов LISP лежит идея о том, что у вас есть определенные «макрофункции», выполняемые во время компиляции. Эти функции генерируют AST (новый код), который будет вставляться в вашу программу. Система макросов в LISP обладает огромным потенциалом, так как позволяет создавать новые языковые конструкции, интеграция которых в язык протекает относительно гладко. Из таких новых конструкций можно собрать предметно-ориентированный язык (DSL), на котором вы затем сможете найти решение конкретной задачи, стоящей перед вами. Почему же у других языков программирования нет такой системы макросов, как у LISP? Загвоздка в том, что реализовывать в других языках подобную систему команд нецелесообразно — во многом потому, что эти языки являются текстовыми.
Исходный код LISP — это тоже текст, но его скобочный формат очень строг и регулярен. Фактически LISP представляет AST-дерево в форме вложенных связанных списков. Программист может создавать код и манипулировать им внутри одного и того же вложенного представления, и даже выполнять сгенерированный код «на лету», пользуясь функцией eval. Одна из основных проблем с LISP-подобными языками заключается в том, что их сложно читать. Я думаю, что удобочитаемость такого языка можно повысить, избавившись от сложного скобочного представления — при программировании должны непосредственно редактироваться базовые структуры данных. Здесь следует оговориться, что, рассуждая о нетекстовых языках программирования, я не имею в виду ни визуальные языки, которые редактируются через один большой сенсорный интерфейс (как в фильме «Особое мнение»), ни «стрелочно-рамочные» языки, в которых для реализации самой небольшой фичи требуется двадцать раз щелкнуть мышью. Я думаю, нетекстовый язык нового поколения должен напоминать усовершенствованную интегрированную среду разработки (IDE), которую можно использовать при помощи старой доброй клавиатуры. Потоки задач в такой IDE будут очень похожи на те, которые сегодня нам уже привычны.
Чего мы этим добьемся, кроме того, что сделаем язык в духе LISP? Во-первых, если в таком языке будут макросы, то их интеграция в основной код будет протекать еще более безболезненно, чем в LISP. Такие конструкции могут иметь собственные настраиваемые визуальные представления. Более того, они могли бы содержать дополнительную семантическую информацию, обеспечивающую более качественную интеграцию с вашей IDE и компилятором. Представьте себе: вы могли бы спроектировать специальный предметно-ориентированный язык для работы с дифференциальным исчислением, который допускал бы визуальное представление с использованием подходящей для этого математической нотации. В вашей IDE с ним были бы ассоциированы соответствующие функции обнаружения ошибок и автозавершения, упрощающие быстрое редактирование. Код автоматически оптимизировался бы на основе правил, которые вы заранее сообщаете компилятору. Вы могли бы не только запросить AST-дерево для функции, но также преобразовывать и инструментировать ее «на лету». Присовокупите к этому возможность визуального форматирования исходного кода любым угодным вам способом, даже не задумываясь о табуляции, пробелах, фигурных скобках, комментировании стилей и тем более о ширине пользовательского экрана.
Я усматриваю очевидную выгоду в том, что мы могли бы редактировать и хранить исходный код в таком формате данных, который более четко передает его семантику. Но одна из важнейших проблем, осложняющих такую практику, — это сложность разработки подходящего редактора. Программисты привыкли писать исходный код в виде текста — по сравнению с такой работой редактирование AST кажется более сложной и кропотливой работой. Редактирование можно было бы упростить и ускорить, если бы такая программа выдавала точные варианты автозавершения. Поскольку редактор имеет непосредственный доступ к AST, варианты автозавершения можно было бы оптимизировать, заложив в редактор подробную семантику языка. Еще одна потенциальная возможность ускорения редактирования — создание собственных комбинаций горячих клавиш, выстраиваемых по известному вам принципу. Например, вы могли бы подготовить собственную коллекцию макросов (функций для генерации кода или шаблонов для деревьев исходников с оставленными в них пробелами). Такие конструкции могли бы легко расширяться, а ваша работа сводилась бы к нажатию всего нескольких клавиш.
Наконец, об инструментальной поддержке. В настоящее время инструментальная поддержка большинства языков программирования оставляет желать лучшего — это касается даже наиболее популярных из них. Отчасти эта проблема объясняется тем, что инструменты должны разбирать и генерировать исходный код. Например, если вам нравятся языки вроде C и C++, то будет непросто отыскать такой синтаксический анализатор, который в полной мере поддерживает грамматику этих языков. Если вы хотите написать инструмент для преобразования кода, то придется решить и такие проблемы, как сохранение комментариев — а это бывает непросто, особенно если выбранный вами синтаксический анализатор для этого не приспособлен. Если бы исходный код представлял собой более однородную структуру данных, то упростилось бы и создание инструментов, которые бы разбирали, анализировали и генерировали код. Было бы проще добавлять метаданные к уже существующему исходному коду. Я считаю, что достоинства такой модели программирования значительно перевешивали бы ее недостатки.
Максим Шевалье-Бойсверт
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.