Я также стоял за увеличение вложений в статический анализ, и это оказалось правильным, хотя сама по себе штука довольно темная. Но мы наняли тех, кто хорошо в нем разбирался.
Сейбел: Статический анализ какого именно вида?
Айк: Анализ языка C++, выполнить который непросто. Обычно при статическом анализе вы анализируете программу целиком и стремитесь, скажем, доказать корректность состояния памяти. Нужно устранить все неоднозначности, а для этого найти в памяти все альтернативные имена - это проблема экспоненциального характера, обычно нерешае-мая ни для одной более-менее крупной программы. Большой прорыв, однако, заключался в том, что больше не надо было волноваться насчет памяти. Если построить полную диаграмму исполнения команд и связать воедино все виртуальные методы с их возможными реализациями, то можно частично оценивать код, не запуская его. Можно найти недостижимый код, можно найти избыточные проверки и пропущенные проверки на NULL.
Можно сделать еще больше на высших уровнях, где работаешь, когда в голове есть система доказательств для программы, которую пишешь. Но в обычных языках нет системы типизации для выражения терминов доказательства. И это серьезная проблема. Согласно Карри-Говарду, существует зависимость между логическими системами и системами типизации, типы являются термами, а программы - доказательствами, и поэтому в принципе можно описать высокоуровневую модель, которую намереваешься создать. Например, такой-то массив на раннем этапе должен иметь ограничение по длине, а на прочих этапах имеет другие ограничения - или не имеет их вовсе. Хитрость отчасти и состоит в том, что на разных этапах действуют разные правила. Или, например, вы надежно защищены внутри своей абстракции и ради большей эффективности нарушаете собственные инварианты - но при этом знаете, что делаете, и знаете, что с внешней точки зрения вы в порядке. Все это очень трудно реализовать в программах с жесткой проверкой типов.
Когда пишешь программу на языке Haskell, приходится выбрать систему доказательств еще до того, как толком поймешь, что именно делаешь. Динамические языки стали популярными, поскольку человек может быстро создать прототипы и держать в голове потенциальную систему типизации. А уже потом, если язык поддерживает это свойство или при перекодировании в статический язык, можно создавать типы. Это одна из причин того, почему мы были заинтересованы в наличии необязательной типизации в JavaScript. Мы заинтересованы в этом до сих пор, хотя среди руководства есть разногласия. Есть неплохие шансы на то, что в будущей версии JavaScript мы получим какую-нибудь смешанную систему типизации.
Поэтому мы хотим снабдить наш C++ аннотациями, на которые можно опираться при консервативном статическом анализе. Именно при консервативном, когда программа не зависает, до бесконечности пытаясь решить проблему экспоненциального свойства. Это поможет нам с проверкой безопасности сборщика мусора или с разделением функций на те, которые получают управление из скриптов, и те, которые передают управление скриптам, или с восстановлением стека интерпретатора, чтобы выносить заключение о безопасности. Мы получим параметры безопасности, которые сможем проверять. Многие из них - это высокоуровневые параметры и относятся не только к безопасности памяти. Поэтому мы должны продолжать борьбу на этом поле.
Сейбел: Да, это весьма высокий уровень программирования. Как по-вашему, насколько близко сегодняшние программисты должны стоять к “железу”? Нужно ли тому, кто в основном пишет приложения на JavaScript, разбираться в языке ассемблера?
Айк: Многие знакомые мне JavaScript-программисты очень умны, и лучшие из них хорошо знакомы с экономическими расчетами. По мере написания кода они измеряют производительность и тестируют, делая все на высоком уровне. Им необязательно знать, как пишутся инструкции для машины.
Многие из них начинают интересоваться этим, когда слышат о компиляции “на лету”, видят создаваемые нами виртуальные машины. И все больше народа работает с графикой. При высокой мощности языка и хороших графических параметрах, думаю, немало JavaScript-программистов начнут пользоваться языком на более низком уровне. И потом, экономические расчеты для физических и виртуальных машин - что важнее? Возможно, расчеты для виртуальных машин.
Абстракция - великая сила. Что у меня вызывает аллергию еще с 1990-х, так это CORBA, COM, DCOM, всякая объектно-ориентированная чушь. Каждый новый проект в то время обязательно имел какую-нибудь примочку, которой было нужно 200 000 вызовов только для запуска и приветствия. Это профанация. Настоящие программисты таким не занимаются. В SGI ядро делали настоящие крутые программисты, и там никто не занимался ерундой. Выделение динамической памяти ядра через malloc было тогда совсем новым делом. Мы использовали таблицы с фиксированным размером и паниковали, заполнив их целиком.
Стоять близко к “железу” для меня лично значило оставаться честным, избегая всякого дерьма. Но со временем, как известно, оборудование стало лучше, мы научились отделять хорошие абстракции от плохих. Наверное, сейчас можно не знать язык ассемблера и при этом быть хорошим программистом, писать грамотный код.
Сейбел: А если взглянуть с обратной стороны: может ли сегодня тот, кто раньше писал замысловатый код на языке ассемблера, успешно заниматься высокоуровневым программированием? Или здесь нужны разные навыки?
Айк: В некоторых областях эти навыки смыкаются. Есть разница между миром простых указателей и солнечным, радостным миром JavaScript. Здесь проходит граница между истинными программистами и всеми прочими.
Важно держать все в голове. Конечно, у всех разная память. Особо памятливые могут держать в уме набор высокоуровневых инвариантов при архитектуре с безопасным доступом к памяти, не заботясь при этом об указателях. Но иногда меня беспокоит, что мы постепенно утрачиваем способность писать для “железа”. Это делают другие; компилятор генерирует код. Видимо, спрос на таких людей будет расти.
Сейбел: Итак, этот вид программирования никуда не денется. А есть ли те, кто может быть успешным программистом сегодня, однако был бы беспомощен в мире низкоуровневого кода? Или программирование требует особого склада ума, и те, кто им обладает, просто разделились на низкоуровневых и высокоуровневых программистов?
Айк: Я давно не занимался кодом ядра, и если нужно будет заняться, это потребует от меня усилий. Теперь нужно писать больше кода. Кроме того, удачные абстракции позволяют сейчас справляться с задачами, не решаемыми в прошлом.
Сейбел: Вернемся к тем десяти дням, когда вы реализовали изначальную версию JavaScript. Я слышал, кто-то обратил ваше внимание на книгу Абельсона и Сассмана[45], и первоначально вы собирались встроить в броузер Scheme.
Айк: Чем прежде всего были озабочены в Netscape? Все должно быть как в Java! Другие создавали алголоподобный синтаксис для Лиспа, но у меня не было времени взять Scheme за основу. Пришлось делать все напрямую, а значит, я мог совершать те же ошибки, что и другие.
Я не стал делать тотальный динамический контекст, на котором настаивал Стеллмен для Emacs и которым он наводнил Elisp. В JavaScript контекст в основном лексический, но есть отступления в виде динамических элементов: глобальный объект, оператор with, функция eval. Но ничего похожего на $- переменные в Perl до введения ту или на команды upvar и uplevel в Tel. В 1990-е все это было очень модно.
Однако я не остановился на Scheme - из-за спешки. Было очень мало времени, чтобы задуматься над последствиями своих действий. Я экономил усилия на многих объектах, которые должны были реализовы- ваться в броузере. Поэтому я сделал window глобальным объектом, который является источником связывания необъявленных новых имен и делает невозможными статические суждения о свободных переменных. А жаль. Дуг Крокфорд и прочие приверженцы объектной модели были недовольны тем, что вы получаете нежелательную власть над глобальным объектом. Это другой способ сказать то же самое. В JavaScript есть безопасные ссылки на объекты в памяти, и мы уже близки к цели, но остаются большие недочеты - те самые отступления.
Эти переменные, привнесенные на высокий уровень, теперь становятся изменяемыми свойствами объекта, которые можно тасовать как угодно за спиной у кого-то - это плохо. Должно быть лексическое связывание. Тогда, если спускаться вниз, к функциям и вложенным функциям, будет больше похоже на Scheme. У вас не будет богатых форм связывания, макросов вроде fluid-let - скорее, что-то вроде set!. Но