Кодеры за работой. Размышления о ремесле программиста - Питер Сейбел 17 стр.


Айк: Это очень занятно. Я был увлечен чистой физикой и не ходил на инженерные занятия, чтобы не иметь дела с пачками перфокарт, которые можно рассыпать - и тогда придется использовать раскладочную машину. Фортран к тому времени меня уже не устраивал. Тогда был очень важен Паскаль, и мы уже начинали осваивать Си. И ассемблер. Я писал низкоуровневый код, делал хеш-таблицы и тому подобное. И это было полезно: так лучше понятны компромиссы, к которым вынуждены прибегать разработчики. Сразу видно, кто работал на уровне битов, а кто всю жизнь работал в защищенной среде.

Меня также интересовали язык Си и UNIX, но с нашим старым железом, то есть DEC, мы только начинали их осваивать. У нас было что-то на основе Portable С Compiler и уасс, мы едва-едва начали генерировать код и занялись портированием программ в UNIX. Физики не получали летней работы, так что я много программировал, был ассистентом в лаборатории, а в последний год сменил специализацию на "математика/компьютерные науки", и именно это значилось в моем дипломе бакалавра.

Сейбел: Помните ли вы свою первую интересную программу?

Айк: Непростой вопрос... Там был жуткий графический терминал от DEC - видимо, усовершенствованный VT100, поскольку он понимал escape-последовательности. Совершенно убогая глубина цвета и разрешение на уровне начала 1980-х. Я начал делать для него копии игр: Рас-Man, Donkey Kong. Эти игры я писал на Паскале, и они использовали escape-последовательности. Это было что-то вроде хобби, которым я занимался все больше и больше. Думаю, это был первый для меня случай нестандартного программирования, когда пришлось задуматься о модульности и защите от самого себя.

Это было тогда, когда я еще специализировался по физике, - наверное, на третьем курсе. На четвертом курсе я сменил специализацию, стал изучать формальные языки и писать генераторы парсеров. Вот так я занимался одновременно вещами серьезными и несерьезными - играми и умными программами. Потом я обратился к компиляторам, стал писать копии макрообработчиков вроде т4 и СРР, все в таком духе. Помню, как мы достали исходник какой-то версии UNIX, как читали по-настоящему заумный код на Си! Препроцессор Си Джона Райзера - возможно, настоящий - был довольно забавной смесью всякой всячины. Он был очень эффективным - использовал глобальный буфер, там было много указателей, и еще он пытался избегать копирования. Я подумал: "Чтобы делать это, должен быть способ получше".

Так я бросил физику и обратился к компьютерным наукам и программированию. До этого я по-настоящему не занимался программированием - только математикой и компьютерной теорией. Родители не хотели покупать мне Apple П. Я заикнулся об этом однажды. Выпрашивать не стал, но сказал, что с помощью компьютера смогу выучить иностранный язык - это была своего рода маскировка. "Нет, ты будешь тратить время на свои игры", - ответили родители и были правы. Вот так они уберегли меня от игр.

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

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

Сейбел: Тут как с испанской инквизицией - никто не ждет плавающую запятую.

Айк: Никто не ожидает встретить получаемые ошибки округления - в пятой степени невообразимые. Округление плохо выполняется в двоичной системе. Поэтому в JavaScript, там, где речь идет о долларах и центах, суммах и разностях, встречаются странные нули с девяткой после них. В одном блоге критиковали Safari и Мак за неправильные математические вычисления. Это стандарт двойной точности IEEE - он встречается везде, и в Java, и в Си.

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

Затем я перешел в Иллинойский университет в Урбана-Шампейне, чтобы наконец получить степень магистра. Я думал, что все уже позади, когда оказался в проекте, для которого IBM набирала специалистов. У них была странная машина 68020, купленная у какой-то компании из Дэнбери (Коннектикут). На этот компьютер они портировали Xenix. Работал он так плохо, что они наняли нашу исследовательскую группу, сделав из нее группу контроля качества. Каждый понедельник появлялся человек в синем костюме и произносил напутственные слова. Преподавателям было, в общем, все равно. Может, я и научился бы чему-нибудь новому, но слышал, о чем говорил в кампусе Джим Кларк, и решил, что хочу работать в Silicon Graphics.

Сейбел: Над чем вы работали в SGI?

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

В конце концов я создал другую реализацию языка - компилятор, который генерировал Си-код на основе описания протокола. Кто-то захотел, чтобы наш анализатор пакетов поддерживал AppleTalk. Это было большое и сложное собрание протокольного синтаксиса для последовательностей и полей разных размеров и зависимых типов... в основном массивов, всякие штуки в этом роде. Это было занятно - трудная задача, которую нужно решить. В итоге мне пригодились некоторые знания о компиляторах из старой "Книги Дракона" Ахо и Ульмана. Думаю, я сделал клон утилиты unifdef. Дэйв Йост уже сделал до меня что-то подобное, но его программа не работала с выражениями #if и не делала минимизацию выражений в том случае, если одни выражения определялись как U, а другие не определялись. Моя программа до сих пор используется и даже, кажется, применяется в Linux.

Я работал в SGI с 1985 по 1992 г. В 1992 г. один мой коллега из SGI перешел в MicroUnity. SGI мне к тому времени уже поднадоела - она все распухала, скупая другие компании, и постепенно переходила под контроль политиков. Так что я оказался в MicroUnity, о которой Джордж Гилдер тогда писал в журнале "Forbes ASAP", что это будет очередной компьютерный монстр. Но все закончилось ничем - компания набрала кредитов на 200 миллионов и обанкротилась. Это было крайне поучительно. Там я занимался компилятором GCC и улучшил свои навыки работы с компиляторами. Еще я писал небольшой язык для редактора видеофайлов MPEG2, на котором можно было писать псевдоспецификации, похожие на стандарты ISO или IEC, и он генерировал тестовые битовые потоки с правильным синтаксисом.

Сейбел: Из MicroUnity вы ушли в Netscape, и дальше все хорошо известно. Скажите, вас устраивает то, как вы учились программированию, или не совсем?

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

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

Что касается программирования, то я всегда говорил, что занимаюсь низкоуровневым программированием. Объектно-ориентированное программирование, шаблоны проектирования - это не для меня. Я так и не купил книгу Эриха Гаммы. Кое-кто в Netscape потрясал этой книгой как Библией - наши с Джейми Завински враги-коллеги, пришедшие в компанию после ее покупки. Просто невыносимо, учитывая, что это были далеко не лучшие программисты.

Сложись все иначе, я мог бы заниматься и высокоуровневыми вещами. Думаю, работая в Mozilla и имея дело с Firefox, я узнал больше о разработке через тестирование - то был ценный опыт. Было и кое-что еще, например тестирование с использованием случайных данных, которого проводилось много. У нас было много исходных языков, были большие и глубокие конвейеры рендеринга, сильно подверженные ошибкам, связанным с безопасностью доступа к памяти. Вообще, тестирование с использованием случайных данных оказалось самым продуктивным видом тестирования.

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

Сейбел: Статический анализ какого именно вида?

Айк: Анализ языка C++, выполнить который непросто. Обычно при статическом анализе вы анализируете программу целиком и стремитесь, скажем, доказать корректность состояния памяти. Нужно устранить все неоднозначности, а для этого найти в памяти все альтернативные имена - это проблема экспоненциального характера, обычно нерешае-мая ни для одной более-менее крупной программы. Большой прорыв, однако, заключался в том, что больше не надо было волноваться насчет памяти. Если построить полную диаграмму исполнения команд и связать воедино все виртуальные методы с их возможными реализациями, то можно частично оценивать код, не запуская его. Можно найти недостижимый код, можно найти избыточные проверки и пропущенные проверки на NULL.

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

Когда пишешь программу на языке Haskell, приходится выбрать систему доказательств еще до того, как толком поймешь, что именно делаешь. Динамические языки стали популярными, поскольку человек может быстро создать прототипы и держать в голове потенциальную систему типизации. А уже потом, если язык поддерживает это свойство или при перекодировании в статический язык, можно создавать типы. Это одна из причин того, почему мы были заинтересованы в наличии необязательной типизации в JavaScript. Мы заинтересованы в этом до сих пор, хотя среди руководства есть разногласия. Есть неплохие шансы на то, что в будущей версии JavaScript мы получим какую-нибудь смешанную систему типизации.

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

Сейбел: Да, это весьма высокий уровень программирования. Как по-вашему, насколько близко сегодняшние программисты должны стоять к "железу"? Нужно ли тому, кто в основном пишет приложения на JavaScript, разбираться в языке ассемблера?

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

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

Абстракция - великая сила. Что у меня вызывает аллергию еще с 1990-х, так это CORBA, COM, DCOM, всякая объектно-ориентированная чушь. Каждый новый проект в то время обязательно имел какую-нибудь примочку, которой было нужно 200 000 вызовов только для запуска и приветствия. Это профанация. Настоящие программисты таким не занимаются. В SGI ядро делали настоящие крутые программисты, и там никто не занимался ерундой. Выделение динамической памяти ядра через malloc было тогда совсем новым делом. Мы использовали таблицы с фиксированным размером и паниковали, заполнив их целиком.

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

Сейбел: А если взглянуть с обратной стороны: может ли сегодня тот, кто раньше писал замысловатый код на языке ассемблера, успешно заниматься высокоуровневым программированием? Или здесь нужны разные навыки?

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

Важно держать все в голове. Конечно, у всех разная память. Особо памятливые могут держать в уме набор высокоуровневых инвариантов при архитектуре с безопасным доступом к памяти, не заботясь при этом об указателях. Но иногда меня беспокоит, что мы постепенно утрачиваем способность писать для "железа". Это делают другие; компилятор генерирует код. Видимо, спрос на таких людей будет расти.

Сейбел: Итак, этот вид программирования никуда не денется. А есть ли те, кто может быть успешным программистом сегодня, однако был бы беспомощен в мире низкоуровневого кода? Или программирование требует особого склада ума, и те, кто им обладает, просто разделились на низкоуровневых и высокоуровневых программистов?

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

Сейбел: Вернемся к тем десяти дням, когда вы реализовали изначальную версию JavaScript. Я слышал, кто-то обратил ваше внимание на книгу Абельсона и Сассмана, и первоначально вы собирались встроить в броузер Scheme.

Айк: Чем прежде всего были озабочены в Netscape? Все должно быть как в Java! Другие создавали алголоподобный синтаксис для Лиспа, но у меня не было времени взять Scheme за основу. Пришлось делать все напрямую, а значит, я мог совершать те же ошибки, что и другие.

Я не стал делать тотальный динамический контекст, на котором настаивал Стеллмен для Emacs и которым он наводнил Elisp. В JavaScript контекст в основном лексический, но есть отступления в виде динамических элементов: глобальный объект, оператор with, функция eval. Но ничего похожего на $-переменные в Perl до введения ту или на команды upvar и uplevel в Tel. В 1990-е все это было очень модно.

Однако я не остановился на Scheme - из-за спешки. Было очень мало времени, чтобы задуматься над последствиями своих действий. Я экономил усилия на многих объектах, которые должны были реализовы-ваться в броузере. Поэтому я сделал window глобальным объектом, который является источником связывания необъявленных новых имен и делает невозможными статические суждения о свободных переменных. А жаль. Дуг Крокфорд и прочие приверженцы объектной модели были недовольны тем, что вы получаете нежелательную власть над глобальным объектом. Это другой способ сказать то же самое. В JavaScript есть безопасные ссылки на объекты в памяти, и мы уже близки к цели, но остаются большие недочеты - те самые отступления.

Эти переменные, привнесенные на высокий уровень, теперь становятся изменяемыми свойствами объекта, которые можно тасовать как угодно за спиной у кого-то - это плохо. Должно быть лексическое связывание. Тогда, если спускаться вниз, к функциям и вложенным функциям, будет больше похоже на Scheme. У вас не будет богатых форм связывания, макросов вроде fluid-let - скорее, что-то вроде set!. Но изначальное связывание, создаваемое при помощи локальной переменной, - это лексическая переменная.

Сейбел: Выходит, сегодня для получения пространств имен создаются высокоуровневые функции?

Айк: Да. Функция создается и тут же вызывается. Это дает безопасную среду для связывания, приватные переменные. Дуг - ярый пропагандист всего этого. Тем, кто работал со Scheme и Лиспом, это уже было отчасти знакомо, но многим JavaScript-программистам пришлось осваивать все с нуля. Дуг и его коллеги провели большую работу по их обучению. Увы, сделать грамотных Scheme-программистов из всех не получилось, но, по крайней мере, люди стали понимать функциональные идиомы, пусть неглубоко, но хотя бы на уровне шаблонов.

Назад Дальше