pg.gimran.org

Пять вопросов о дизайне языков программирования

Май 2001 · Все эссе

Перевод эссе Пола Грэма «Пять вопросов о дизайне языков программирования». Оригинал: https://paulgraham.com/langdes.html. Машинный перевод (Gemini).

Translation of Paul Graham's essay 'Five Questions about Language Design'. Original: https://paulgraham.com/langdes.html. Machine translation (Gemini).

(Это заметки, которые я подготовил для панельной дискуссии о дизайне языков программирования в MIT 10 мая 2001 года.)

1. Языки программирования создаются для людей.

Языки программирования — это способ общения людей с компьютерами. Компьютеру было бы совершенно все равно, на каком языке общаться, лишь бы он был однозначным. Причина, по которой у нас есть языки высокого уровня, заключается в том, что люди не справляются с машинным языком. Смысл языков программирования в том, чтобы уберечь наш бедный, хрупкий человеческий мозг от перегрузки массой деталей.

Архитекторы знают, что некоторые задачи проектирования носят более личный характер, чем другие. Одна из самых чистых и абстрактных задач — проектирование мостов. Там ваша работа в основном сводится к тому, чтобы перекрыть заданное расстояние с минимальным расходом материалов. На другом конце спектра — проектирование стульев. Создателям стульев приходится думать о человеческих задницах.

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

Большинству из нас неприятно это признавать. Проектирование систем поразительной математической элегантности звучит для большинства куда привлекательнее, чем потакание человеческим слабостям. И математическая элегантность действительно играет свою роль: определенные ее виды облегчают понимание программ. Но элегантность не является самоцелью.

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

2. Проектируйте для себя и своих друзей.

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

Когда языки проектируются для других людей, это всегда определенная группа: люди, которые не так умны, как создатель языка. В итоге получается язык, который говорит с вами свысока. Cobol — самый крайний случай, но этим духом пропитаны многие языки.

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

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

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

3. Давайте программисту как можно больше контроля.

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

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

4. Стремитесь к краткости.

Краткость недооценивают и даже презирают. Но если заглянуть в душу хакерам, вы увидите, как искренне они ее любят. Сколько раз вы слышали, как хакеры с восторгом рассказывали о том, как, скажем, в APL они могли делать невероятные вещи всего парой строк кода? Я думаю, что все, что по-настоящему любят по-настоящему умные люди, заслуживает внимания.

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

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

5. Признайте, чем на самом деле является хакерство.

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

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

1. Как организовать большие библиотеки?

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

2. Неужели люди действительно боятся префиксного синтаксиса?

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

3. Что требуется для серверного программного обеспечения?

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

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

Вы знаете, что программы можно проектировать удобными для отладки? Так вот, серверное ПО точно так же должно проектироваться удобным для изменений. Вы должны иметь возможность легко его менять или как минимум понимать, какое изменение будет мелким, а какое — судьбоносным.

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

4. Какие новые абстракции нам еще предстоит открыть?

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

1. Вы можете использовать любой язык, какой захотите.

Раньше разработка прикладных программ означала создание десктопного ПО. А в десктопном софте всегда существовал сильный перекос в сторону написания приложений на том же языке, что и операционная система. И десять лет назад разработка программ почти всегда означала программирование на C. Постепенно сложилась традиция: прикладные программы нельзя писать на необычных языках. И эта традиция формировалась так долго, что ее усвоили даже нетехнические специалисты, вроде менеджеров и венчурных капиталистов.

Серверное программное обеспечение напрочь разрушает эту модель. С серверным софтом вы можете использовать абсолютно любой язык, какой захотите. Этого пока почти никто не понимает (особенно менеджеры и венчурные капиталисты). Это понимают лишь немногие хакеры, и именно поэтому мы вообще слышим о новых независимых языках вроде Perl и Python. Мы слышим о Perl и Python вовсе не потому, что люди пишут на них приложения для Windows.

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

2. Скорость достигается с помощью профилировщиков.

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

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

3. Дизайн языка должно направлять реальное приложение.

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

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

[Во время дискуссии Гай Стил также подчеркнул эту мысль, добавив предложение: приложением не должно быть написание компилятора для вашего же языка, если только ваш язык изначально не предназначается для создания компиляторов.]

4. Язык должен хорошо подходить для написания одноразовых программ.

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

5. Синтаксис связан с семантикой.

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

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

1. Новые языки программирования.

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

2. Разделение времени.

На прошлой панели Ричард Келси назвал это идеей, чье время снова пришло, и я полностью с ним согласен. Мое предположение (и, похоже, предположение Microsoft) заключается в том, что значительная часть вычислений переместится с десктопов на удаленные серверы. Другими словами, разделение времени возвращается. И я думаю, ему потребуется поддержка на уровне языка. Например, я знаю, что Ричард и Джонатан Рис проделали большую работу, реализовав планирование процессов внутри Scheme 48.

3. Эффективность.

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

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

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

1. Клиенты.

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

Я думаю, произойдет взрывное распространение устройств с тем или иным доступом к Сети, и все, что вы сможете о них предполагать, — это способность поддерживать простой HTML и формы. Будет ли браузер в вашем мобильном телефоне? Будет ли телефон в вашем карманном компьютере Palm? Станет ли экран у вашего BlackBerry больше? Сможете ли вы бродить по Сети на Game Boy? На часах? Я не знаю. И мне не нужно этого знать, если я сделаю ставку на то, что все находится на сервере. Держать все мозги на сервере куда надежнее.

2. Объектно-ориентированное программирование.

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

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

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

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

3. Проектирование комитетом.

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

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