Я, как и почти каждый Common Lisp и CLOS разработчик, очень хорошо знаком с подходом к проектированию MIT/Стэнфорда. Суть этого подхода можно выразить фразой “the right thing” (то, что нужно/как следует). Для таких разработчиков важно соответствовать всем нижеизложенным характеристикам:
Простота (simplicity) — архитектура, с точки зрения реализации и интерфейса, должна быть простой. В данном походе простота интерфейса важнее, чем простота реализации.
Правильность (correctness) — архитектура должна быть правильна во всех наблюдаемых аспектах. Неправильная архитектура просто недопустима.
Логичность (consistency) — архитектура должна быть логичной. Во избежание нелогичности допускается пожертвовать простотой и полнотой архитектуры. Характеристики - логичность и правильность - важнее, чем простота и полнота.
Полнота (completeness) — архитектура должна охватывать как можно больше важных ситуаций с учетом практической целесообразности. Все допустимые кейсы должны быть предусмотрены. Простота не должна слишком мешать полноте.
Думаю, большинство людей согласятся, что это хорошие характеристики. Я назвал следование этой архитектурной философии - подходом MIT (Massachusetts Institute of Technology). Common Lisp (с CLOS) и Scheme представляют подход MIT к проектированию и реализации.
Философия “the worse-is-better” (чем хуже, тем лучше) не так уж сильно отличается:
Простота — архитектура, с точки зрения реализации и интерфейса, должна быть простой. Простота реализация важнее, чем простота интерфейса. Простота -- это самое важное требование в архитектуре.
Правильность — архитектура должна быть правильна во всех наблюдаемых аспектах. Простая архитектура лучше, чем правильная.
Логичность — архитектура должна быть логичной. В некоторых случаях логичностью можно пожертвовать ради простоты, но лучше отказаться от тех частей архитектурыа, которые будут полезны не во всех ситуациях, чем усложнить реализацию, или пожертвовать логичностью.
Полнота — архитектура должна охватывать как можно больше важных ситуаций с учетом практической целесообразности. Все допустимые кейсы должны быть предусмотрены. Полнотой можно пожертвовать в пользу любой другой характеристики. По сути, нужно жертвовать полнотой всякий раз, когда она мешает простоте реализации. Логичностью можно жертвовать для достижения полноты, если при этом сохраняется простота; логичность интерфейса в этом подходе наиболее незначительна.
Ранние версии системы Unix и языка C являются примерами использования этой архитектурной школы, а использование этой архитектурной стратегии я назвал - подходом Нью-Джерси. Я намеренно карикатурно описал философию “worse-is-better”, чтобы доказать вам, что эта философия и этот подход - плохой подход.
Однако, по моему мнению, “worse-is-better” (даже в такой перевернутой форме), все таки имеет лучшие характеристики живучести, чем “right-thing”, и для программного обеспечения подход Нью-Джерси подойдет лучше, чем подход MIT.
Позвольте мне начать с пересказа истории, которая покажет вам, что различие между MIT и Нью-Джерси действительно существует, и как сторонники каждой из этих философий твердо убеждены, что их философия является лучшей.
Два известных человека, один из Массачусетского технологического института, другой из Беркли (но работающий над Unix), однажды встретились, чтобы обсудить проблемы, связанные с операционными системами. Человек из MIT был осведомлен об ITS (операционной системе MIT AI Lab) и читал исходный код Unix. Его интересовало, как Unix решает проблему PC loser-ing. Проблема PC loser-ing возникает, когда пользовательская программа вызывает системную функцию для выполнения длительной операции, в результате которой изменение состояния может быть существенно, например буферы ввода-вывода. Если во время работы возникает прерывание, состояние пользовательской программы должно быть сохранено. Поскольку вызов системной функции обычно представляет собой единичную инструкцию, счётчик команд (program counter) пользовательской программы не может корректно фиксировать состояние процесса. Системная функция должна либо откатиться, либо форсануть вперед. Right thing предполагает откат и восстановление ПК пользовательской программы по инструкции, которая вызвала системную функцию, например, чтобы возобновление пользовательской программы повторно запустило системную функцию после прерывания. Такая ситуация называется PC loser-ing, потому что счётчик команд переводится в loser mode, где loser - “нежное” имя для пользователя в MIT.
Парень из MIT не встречал кода, который обрабатывал бы этот случай, и спросил парня из Нью-Джерси, как решается такая проблема. Парень из Нью-Джерси сказал, что сотрудники Unix знают об этой проблеме, а решение заключается в том, чтобы системная функция завершалась всегда, но иногда получаемый код ошибки сигнализировал бы о том, что системная функция не смогла завершить свое действие. Таким образом, правильная пользовательская программа должна осуществлять проверку на наличие кода ошибки, чтобы определить, есть ли необходимость повторно запускать системную функцию. Парню из MIT не понравился такой способ решения проблемы, ведь оно не соответствовало right thing подходу.
Парень из Нью-Джерси сказал, что решение Unix было правильным, потому что философия проектирования Unix заключалась в простоте, а пойти путем right thing было слишком сложно. Кроме того, программисты могли легко вставить этот дополнительный тест и цикл. Парень из MIT указал, что реализация была простой, но интерфейс функциональности сложен. Парень из Нью-Джерси сказал, что в Unix был выбран правильный компромисс, а именно, простота реализации для них была важнее простоты интерфейса.
Затем парень из MIT пробормотал, что иногда требуется крепкий мужчина, чтобы приготовить нежную курочку, но парень из Нью-Джерси не понял (я тоже не уверен, что до-конца понимаю о чем речь).
А теперь я хотел бы привести парочку аргументов в пользу философии “worse-is-better”. Язык C - это язык программирования, предназначенный для написания системы Unix, и он был разработан с использованием подхода Нью-Джерси. Для языка C легко написать годный компилятор, и он требует, чтобы программист написал код, который легко интерпретирует этот компилятор. Некоторые называют C навороченным языком ассемблера. Ранние компиляторы Unix, как и компиляторы C имели простую структуру, их было легко переносить, и они требовали небольшого количества машинных ресурсов для работы, обеспечивая от 50% до 80% того, что вам было нужно от операционной системы и языка программирования.
Половина компьютеров, которые когда-либо существовали, находятся ниже медианы (меньше или медленнее). Unix и C отлично работают на них. Философия “worse-is-better” подразумевает, что простота реализации имеет наивысший приоритет, что означает, что Unix и C легко переносятся на такие машины. Неудивительно то, что если бы поддержка 50% функционала Unix и C была организована на хорошем уровне, они бы использовались повсеместно. Что мы и наблюдали, не так ли?
Unix и C - ультимативные компьютерные вирусы
Еще одним преимуществом философии “worse-is-better” является то, что программист вынужден жертвовать некоторой безопасностью, удобством и временем, для того, чтобы добиться хорошей производительности и умеренного использования ресурсов. Программы, написанные с использованием подхода Нью-Джерси, будут хорошо работать как на маленьких, так и на больших машинах, а код будет переносимым, потому что он написан поверх вируса.
Важно помнить, что исходный вирус должен быть в корне очень хорошим. Если это так, то распространение вируса гарантировано, пока он портативен. После успешного распространения вируса, возникнет необходимость улучшить его, возможно, за счет увеличения его функциональности до 90% от желаемого, но пользователи уже были приучены принимать скорее худшие, чем right thing решения. Следовательно, программное обеспечение “worse-is-better” первым делом, получит признание, далее, заставит пользователей ожидать меньшего, и напоследок, будет улучшено до такой степени, что практически станет соответствовать right thing. Можно привести конкретный пример - хоть компиляторы Lisp в 1987 году были примерно так же хороши, как и компиляторы C, эксперты по компиляторам хотят улучшить компиляторы C гораздо больше, чем компиляторы Lisp.
Хорошая новость заключается в том, что в 1995 году у нас появятся хорошие операционная система и язык программирования; плохая новость в том, что это будут Unix и C++.
Осталось еще одно преимущество философии “worse-is-better”. Поскольку язык Нью-Джерси и его система на самом деле недостаточно мощны для создания сложного монолитного программного обеспечения, большие системы должны разрабатываться, полагаться на повторное использование компонентов. Таким образом, возникает традиция интеграции.
Как с этим обстоят дела у right thing? Есть два основных сценария: сценарий “большая сложная система” (big complex system) и сценарий “драгоценность, похожая на бриллиант” (diamond-like jewel).
Сценарий “большая сложная система” выглядит следующим образом:
Во-первых, нужно спроектировать right thing. Затем нужно спроектировать ее реализацию. В конечном итоге она будет реализована. Поскольку это right thing, она обеспечивает почти 100% желаемой функциональности, а так как простота реализации никогда не была в приоритете, ее реализация занимает много времени. Система получается большой и сложной. Для правильного использования ей требуются сложные инструменты. На последние 20% уходит 80% усилий, поэтому right thing занимает много времени, и удовлетворительно работает только на самом крутом аппаратном обеспечении.
Сценарий “драгоценность, похожая на бриллиант” выглядит следующим образом:
На создание right thing уходит вечность, и на каждом этапе прогресс невелик. Реализовать данную систему так, чтобы она работала быстро, либо невозможно, либо это выходит за рамки возможностей большинства разработчиков.
Эти два сценария соответствуют Common Lisp и Scheme.
Первый сценарий также является сценарием для классического программного обеспечения искусственного интеллекта.
Right thing часто представляет собой монолитную часть программного обеспечения. Но right thing и без того часто разрабатывается монолитно. То есть эта характеристика - естественное следствие.
Вывод, который следует извлечь из этого - нежелательно пытаться делать right thing сразу. Лучше для начала сделать так, чтобы только половина right thing уже отправлялась пользователю, чтобы она распространялась как вирус. Как только люди привыкнут к такому софту, нужно будет найти время, чтобы улучшить софт до 90% от right thing.
Неправильно воспринимать все буквально и делать вывод, что язык C - правильный инструмент для программного обеспечения AI. Решение на 50% должно быть в основном правильным, а в данном случае это не так.
Но можно сделать вывод о том, что сообществу Lisp необходимо переосмыслить свою позицию в отношении архитектуры Lisp. Подробнее об этом я расскажу позже.
Перевод статьи подготовлен в преддверии старта курса "Программист С".