В современных компьютерных системах есть несколько других уровней иерархии. Например, некоторые системы — часто называемые «микрокомпьютерами» — используют еще более рудиментарные команды на машинном языке, чем добавка числа в памяти к числу в регистре. Пользователь должен сам решать, какой тип команд на обычном машинном языке он хочет запрограммировать; он «микропрограммирует» эти команды в терминах имеющихся у него «микрокоманд» После этого разработанные им команды на языке высшего уровня могут быть включены в схему компьютера и стать частью аппаратуры, хотя это и не обязательно. Подобное микропрограммирование позволяет пользователю спуститься немного ниже уровня обычного машинного языка. Одним из следствий этого является то, что какой-либо компьютер одной фирмы может, путем микропрограммирования, быть снабжен такой аппаратурой, что она повторяет машинные команды другого компьютера той же (или даже иной) фирмы. При этом говорится, что компьютер с микропрограммой имитирует другой компьютер.
Далее, у нас имеется уровень операционной системы, который расположен между уровнями программы на машинном языке и следующим уровнем, на котором программирует пользователь. Операционная система — это программа, предотвращающая доступ пользователей к самой машине (и таким образом защищающая систему); эта программа избавляет пользователя от многих сложных и запутанных проблем, таких, как прочтение программы, вызов программы-переводчика, выполнение переведенной программы, направление результата по нужным каналам в нужное время и передача контроля следующему пользователю. В случае, когда с ЦП говорят сразу несколько пользователей, операционная система переключает внимание ЦП в определенном порядке. Операционные системы удивительно сложны; здесь я только намекну на эти сложности при помощи следующей аналогии.
Рассмотрим первую телефонную систему. Александр Грэхем Белл мог позвонить своему ассистенту в соседнюю комнату: электронная передача голоса! Это сравнимо с простым компьютером без операционной системы: электронные вычисления!
Рассмотрим теперь современную телефонную систему. У вас есть выбор, с каким телефоном соединиться; к тому же, можно отвечать на многие звонки одновременно. Вы можете добавить код и соединиться с другими районами. Вы можете позвонить прямо или через оператора; так, что звонок будет оплачен вашим собеседником или по вашей кредитной карточке. Можно говорить с одним человеком или сразу с несколькими; можно «перенаправить» или проследить звонок. Существует сигнал «занято», сигнал, говорящий вам, что набранный номер не является «хорошо сформированным» и сигнал, говорящий вам, что вы набирали номер слишком долго. Вы можете установить местный коммутатор, соединяющий несколько телефонов, — и так далее, и тому подобное. Это удивительный список, если подумать, сколько возможностей он представляет, в особенности, по сравнению с былым чудом «голого» телефона. Вернемся теперь к компьютерам: сложные операционные системы выполняют примерно те же операции направления трафика и переключения уровней по отношению к пользователям и их программам. Мы можем быть практически уверены в том, что у нас в мозгу происходят некие параллельные процессы, одновременная обработка многих стимулов; решения о том, что должно выйти на первый план и на какое время; мгновенные «перерывы» из-за неожиданных событий и критических положений и так далее.
Забота о пользователе и защита системы
Многие уровни сложной компьютерной системы, взятые вместе, облегчают пользователям их работу, позволяя им не думать о процессах, происходящих на низших уровнях (которые, скорее всего, для них совершенно неважны). Пассажир в самолете обычно не интересуется уровнем горючего в баках, скоростью ветра, количеством куриных крылышек, которые будут поданы на ужин пассажирам, или воздушным трафиком около места назначения. Все это — дело служащих на разных уровнях иерархии авиакомпании; пассажир же хочет только одного: чтобы его доставили из одного места в другое. Только когда случается что-нибудь непредвиденное, например, потеря багажа, пассажир понимает, с какой запутанной системой уровней он имеет дело.
Компьютеры — супергибкость или супержесткость?
Одной из основной целей в нашем стремлении к высшим уровням всегда было желание сообщать компьютеру о том. чего мы от него хотим, самым естественным для нас образом. Безусловно, конструкции высшего уровня в языках компиляторах ближе к категориям, в которых обычно думают люди, чем конструкции низшего уровня, такие, как в машинных языках. Но в этом стремлении к легкости общения с компьютерами мы обычно забываем об одном из аспектов «естественности», — а именно, том факте, что общение между людьми имеет намного меньше ограничений, чем общение между человеком и машиной. Например, мы зачастую произносим бессмысленные словосочетания, ища, как бы получше выразить свою мысль, кашляем в середине фразы, перебиваем друг друга, используем двусмысленные описания и «неправильный» синтаксис, придумываем выражения и искажаем смысл — но наши сообщения обычно все же достигают цели. В языках программирования, напротив синтаксис должен быть стопроцентно строгим, в них не должно быть двусмысленных выражений и конструкций. Интересно, что печатный эквивалент кашля разрешен, но только если он предварен условным знаком (например, словом КОММЕНТАРИЙ), после него также должен иметься условный знак (например, точка с запятой). Ирония в том, что эта небольшая уступка гибкости создает свои проблемы: если точка с запятой (или любой другой условный знак, отмечающий конец комментария) встречается внутри комментария, программа переводчик интерпретирует ее, как сигнал окончания комментария, после чего следует полная неразбериха.
Представьте, что в программе определена процедура под названием ПОНИМАНИЕ, и эта процедура затем вызвана семнадцать раз. Если в восемнадцатый раз это слово ошибочно написано ПОМИНАНИЕ, горе программисту! Компилятор взбунтуется и напечатает весьма неприятное послание ОШИБКА, сообщая, что он никогда не слыхал ни о каком ПОМИНАНИИ. Часто, когда компилятор обнаруживает подобную ошибку, он пытается продолжить работу, но из-за отсутствия у него поминания, он не может понять, что имел в виду программист. На самом деле, он может даже вообразить, что тот имел в виду нечто совершенно другое, и начать действовать согласно этой ошибочной интерпретации. В результате, остальная программа будет усеяна посланиями «ошибка», потому что компилятор — а не программист — запутался. Вообразите, какая путаница получится, если, переводя с английского на русский, переводчик услышит фразу по-французски и попытается переводить остальной английский текст, как французский! Компиляторы часто запутываются таким жалким образом. C'est la vie.
Может быть, это звучит как приговор компьютерам, — но я вовсе не имел это в виду. В некотором смысле, такое положение вещей необходимо. Если подумать, для чего обычно используются компьютеры, становится ясно, что они выполняют весьма определенные и точные задания, которые слишком сложны для людей. Чтобы мы могли доверять компьютерам, необходимо, чтобы они совершенно точно, без следа двусмысленности, понимали, что от них требуется. Необходимо также, чтобы компьютер делал не больше и не меньше того, что ему приказано. Если между компьютером и программистом стоит программа, предназначенная угадывать, чего тот хочет или имеет в виду, то весьма вероятно, что, когда программист попытается сообщить машине задачу, она будет понята совершенно неверно. Таким образом важно, чтобы программы высшего уровня хотя и удобные для людей, тем не менее были бы недвусмысленными и точными.
Как предвосхитить желания пользователя
Несмотря на это, возможно создать язык программирования который допускает некоторый тип неточности и программу, переводящую его на низшие уровни. Можно сказать, что программа-переводчик при этом будет пытаться интерпретировать нечто, сделанное «вне правил языка». Но если в языке допускаются некие «нарушения» правил, подобные нарушения уже нельзя назвать настоящими нарушениями, поскольку они включены в правила! Если программисту разрешено допускать определенный тип ошибок, он может использовать эту черту, зная, что при этом он оперирует строго в рамках правил, несмотря на видимость обратного. Иными словами, если пользователь знает о всех трюках, делающих программу-переводчика более гибкой и удобной для пользования, то он знает и предел, который он не может перейти; следовательно, ему эта программа все равно кажется жесткой и негибкой, хотя она и дает ему гораздо большую свободу по сравнению с ранними версиями, не включавшими «автоматическую компенсацию человеческих ошибок.»