(Это первая глава книги ANSI Common Lisp, автор Пол Грэм. Copyright 1995, Prentice-Hall).
Введение
Джон Маккарти и его студенты начали работу над первой реализацией Lisp в 1958 году. После Фортрана Lisp является самым старым языком. который до сих пор используется. [1] Что еще более примечательно, так это то, что он все еще находится в в авангарде технологий языков программирования. Программисты, которые знают Lisp, скажут вам, что в этом языке есть нечто, что что отличает его от других.
Отчасти отличительной чертой Lisp является то, что он разработан для развиваться. Вы можете использовать Lisp для определения новых операторов Lisp. По мере того, как новые абстракции становятся популярными (объектно-ориентированное программирование, например. например), всегда оказывается, что их легко реализовать в Lisp. Подобно ДНК, такой язык не выходит из моды.
Новые инструменты
Зачем изучать Lisp? Потому что он позволяет вам делать то, что вы не можете сделать на других языках. Если вы просто хотите написать функцию, возвращающую сумму чисел, меньших n, скажем, это будет выглядеть одинаково одинаково в Lisp и С:
; Lisp /* C */ (defun sum (n) int sum(int n){ (let ((s 0)) int i, s = 0; (dotimes (i n s) for(i = 0; i < n; i++) (incf s i)))) s += i; return(s); }
Если вам нужно делать только такие простые вещи, не имеет значения. какой язык вы используете. Предположим, вместо этого вы хотите написать функцию которая принимает число n и возвращает функцию, которая прибавляет n к своему аргументу:
; Lisp (defun addn (n) #'(lambda (x) (+ x n)))
Как выглядит addn в С? Вы просто не можете его написать.
Возможно, вы зададитесь вопросом, когда человек вообще захочет делать такие вещи. это? Языки программирования учат вас не хотеть того, что они не могут предоставить. Вы должны думать на языке, чтобы писать на нем программы, а трудно хотеть того, что не можешь описать. Когда я впервые начал писать программы - на Basic - я не скучал по рекурсии, потому что я не знал, что есть такая вещь. Я думал на Basic. Я мог представить себе только итеративные алгоритмы, так почему я должен скучать по рекурсию?
Если вы не скучаете по лексическим закрытиям (а это то, что делается в предыдущем примере), примите на веру, пока что, что программисты Lisp используют их постоянно. Было бы трудно найти программу на Common Lisp любой длины, которая не использовала бы преимущества закрытия. К странице 112 вы будете использовать их сами.
И замыкания - это только одна из абстракций, которых нет в других языках. Другая уникальная особенность Lisp, возможно, даже более ценная. более ценная, заключается в том, что программы на Lisp выражаются в виде структур данных Lisp. структуры данных. Это означает, что вы можете писать программы, которые пишут программы. Действительно ли люди хотят это делать? Да - они называются макросы, и, опять же, опытные программисты используют их постоянно. К странице 173 вы сможете написать свои собственные.
С макросами, закрытиями и типизацией во время выполнения Lisp выходит за рамки объектно-ориентированного программирования. Если вы поняли предыдущее предложение, вам, вероятно, не стоит читать эту книгу. Вы должны хорошо знать Lisp, чтобы понять, почему это так. Но это не просто слова. Это важный момент, и доказательство этого достаточно ясно изложено в коде в главе 17.
В главах 2--13 постепенно будут введены все понятия, которые понадобятся вам для понимания кода в главе 17. Награда за ваши усилия будет неоднозначной: вы будете чувствовать себя настолько же удушенным, программируя на C++, насколько опытный программист на C++ чувствовал бы себя удушенным, программируя на Basic. программируя на Basic. Возможно, это будет более обнадеживающим, если мы подумаем о том. о том, откуда берется это чувство. Basic является удушающим для человека, привыкшего к C++, потому что опытный программист на C++ знает приемы, которые невозможно выразить в Basic. Аналогично, изучение Lisp научит вас не просто новому языку - оно он научит вас новым и более мощным способам мышления о программах.
Новые методы
Как объяснялось в предыдущем разделе, Lisp предоставляет вам инструменты, которые другие другие языки не предоставляют. Но это еще не все. Если брать по отдельности, то новые вещи, которые приходят с Lisp - автоматическое управление памятью. управление памятью, явная типизация, закрытия и т.д. - каждое из них делает программирование намного проще. Взятые вместе, они образуют критическую массу, которая делает возможным новый способ программирования.
Lisp разработан как расширяемый: он позволяет вам самостоятельно определять новые операторы. самостоятельно. Это возможно потому, что язык Lisp состоит из из тех же функций и макросов, что и ваши собственные программы. Поэтому расширить Lisp не сложнее расширить Lisp, чем написать на нем программу. На на самом деле, это настолько просто (и настолько полезно), что расширение языка является стандартной практикой. По мере того, как вы пишете свою программу вниз к языку языку, вы строите язык вверх к вашей программе. Вы работаете как снизу вверх, так и сверху вниз.
Почти любая программа может выиграть от того, что язык будет адаптирован под ее нужды, но чем сложнее программа, тем более ценнее становится программирование снизу вверх. Программа "снизу вверх" может быть может быть написана как серия слоев, каждый из которых действует как своего рода язык программирования для предыдущего. TeX был одной из самых ранних программ, написанных таким образом. Вы можете писать программы снизу вверх на любом языке, но Lisp является наиболее естественным средством для этого стиля. стиля.
Программирование снизу вверх естественно ведет к расширяемому программному обеспечению. Если вы используете принцип программирования снизу вверх до самого верхнего слоя вашей программы, то этот слой становится языком программирования для пользователя. Поскольку идея расширяемости так глубоко укоренилась в Lisp, он является идеальным языком для написания расширяемого программного обеспечения. Три наиболее успешные программы 1980-х годов используют Lisp в качестве языка расширения: Gnu Emacs, Autocad и Interleaf.
Работа снизу вверх также является лучшим способом получения многоразового программного обеспечения. Суть написания многоразового программного обеспечения заключается в отделении общего от конкретного, а программирование снизу вверх по своей сути создает такое разделение. Вместо того чтобы направлять все свои усилия на написание единого монолитного приложения, вы посвящаете часть своих усилий созданию языка, а часть - написанию (пропорционально меньшего) приложения на его основе. То, что специфично для этого приложения будет сосредоточено в самом верхнем слое. Слои под ним сформируют язык для написания приложений, подобных этому а что может быть более многоразовым, чем язык программирования?
Lisp позволяет вам не только писать более сложные программы, но и но и писать их быстрее. Программы на Lisp, как правило, короткие - язык дает вам больше понятий, поэтому вам не нужно использовать их так много. Как отмечает Фредерик Брукс отметил, что время, необходимое для написания программы программы зависит в основном от ее длины. Поэтому один этот факт означает. что написание программ на Lisp занимает меньше времени. Эффект усиливается динамическим характером Lisp: в Lisp цикл редактирования-компиляции-тестирования настолько короток, что программирование происходит в режиме реального времени.
Большие абстракции и интерактивная среда могут изменить способ разработки программного обеспечения в организациях. Фраза "быстрое прототипирование" описывает вид программирования, который начался с Lisp: на Lisp вы часто можете написать прототип за меньшее время, чем требуется для написания спецификации. Более того, такой прототип может быть настолько абстрактным, что его спецификация будет лучше, чем спецификация, написанная на английском языке. Кроме того, Lisp позволяет вам плавно перейти от прототипа к производственному программному обеспечению. Когда программы на Common Lisp написаны с с учетом скорости и компилируются современными компиляторами, они работают так же быстро. как и программы, написанные на любом другом языке высокого уровня.
Если вы не знаете Lisp достаточно хорошо, это введение может показаться вам набором грандиозных и, возможно, бессмысленных заявлений. Lisp превосходит объектно-ориентированное программирование? Вы строите язык вверх к своим программам? Программирование на Lisp возможно в реальном времени? Что могут означать заявления? На данный момент эти утверждения подобны пустым озерам. По мере того, как вы узнаете больше о реальных возможностях Lisp и увидите примеры работающих программ, они наполнятся реальным опытом и примут определенную форму.
Новый подход
Одна из целей этой книги - объяснить не только язык Lisp. язык, но и новый подход к программированию, который Lisp делает возможным возможным. Этот подход - один из тех, которые вы будете чаще видеть в будущем. По мере того, как среды программирования становятся все мощнее, а языки становятся более абстрактными, стиль программирования на Lisp постепенно вытесняет старую модель "план и реализация".
В старой модели ошибки никогда не должны случаться. Тщательные спецификации, кропотливо разработанные заранее, должны гарантировать, что программы будут работать идеально. В теории звучит хорошо. К сожалению, спецификации и пишутся, и реализуются людьми. В результате на практике метод планирования и реализации работает не очень хорошо.
Как руководитель проекта OS/360, Фредерик Брукс был хорошо знаком с традиционным подходом. Он также был знаком с его результатами:
И это описание одной из самых успешных систем своей эпохи. своей эпохи.
Проблема старой модели заключалась в том, что она игнорировала человеческие ограничения. В старой модели вы делаете ставку на то, что спецификации не будут содержать серьезных недостатков, и что их реализация будет простым вопросом перевести их в код. Опыт показал, что это очень плохая ставка. Безопаснее было бы сделать ставку на то, что спецификации будут ошибочными, а код будет полон ошибок.
Именно это и предполагает новая модель программирования. Вместо этого чтобы надеяться, что люди не будут делать ошибок, она пытается сделать так, чтобы стоимость ошибок очень низкой. Стоимость ошибки - это время необходимое для ее исправления. С помощью мощных языков и хороших сред программирования среды программирования, эта стоимость может быть значительно снижена. Стиль программирования может в меньшей степени зависеть от планирования и в большей - от исследования.
Планирование - это необходимое зло. Это реакция на риск: чем опаснее опасное предприятие, тем важнее планировать его заранее. Мощные инструменты снижают риск, а значит, снижают необходимость планирования. При разработке вашей программы можно воспользоваться тем, что, возможно, является самым полезным источником информации: опыт ее реализации.
Стиль Lisp развивается в этом направлении с 1960-х годов. В Lisp можно писать прототипы так быстро, что вы можете пройти через несколько итераций проектирования и реализации, прежде чем вы, в старой модели, даже не закончив писать спецификации. Вам не нужно так сильно беспокоиться о недостатках проектирования, потому что вы обнаруживаете их гораздо раньше. Вам также не нужно беспокоиться о ошибках. Когда вы программируете в функциональном стиле, ошибки могут иметь только локальный эффект. Когда вы используете очень абстрактный язык, некоторые ошибки (например, висячие указатели) становятся невозможными, а те, что остаются. легко найти, потому что ваши программы намного короче. И когда у вас есть интерактивная среда, вы можете исправить ошибки мгновенно, вместо того чтобы проходить длинный цикл редактирования, компиляции, и тестирования.
Стиль Lisp развивался таким образом, потому что он приносит результаты. Как бы странно это ни звучало, но меньше планирования может означать лучший дизайн. История технологий полна параллельных примеров. Аналогичные изменения произошли в живописи в пятнадцатом веке. До того, как масляная краска стала популярной, художники использовали темперу, которую нельзя было смешивать или перекрашивать. Цена ошибки была высока, и это заставляло художников быть консервативными. Затем появилась масляная краска, а вместе с ней и большие изменения в стиле. Масло "позволяет передумать". [3] Это оказалось решающим преимуществом при работе с такими сложными сюжетами, как человеческая фигура.
Новое средство не просто облегчило жизнь художников. Оно сделало возможным новый и более амбициозный вид живописи. Янсон пишет:
Как материал, темпера не менее красива, чем масло. Но гибкость масляной краски дает больший простор для воображения... это и стало решающим фактором.
В настоящее время программирование претерпевает аналогичные изменения. Новым средством является "объектно-ориентированный динамический язык" - одним словом, Lisp. Это не означает, что все наше программное обеспечение будет написано на Lisp в течение нескольких лет. Переход от темперы к маслу не произошел не в одночасье; сначала масло было популярно только в ведущих художественных центрах, и часто использовалось в сочетании с темперой. Мы Похоже, сейчас мы находимся на этой стадии. Lisp используется в университетах, исследовательских лабораториях и в нескольких передовых компаниях. Тем временем идеи, заимствованные из Lisp, все чаще появляются в мейнстриме: интерактивные среды программирования, сборка мусора и типизация во время выполнения, вот лишь некоторые из них.
Более мощные инструменты избавляют от риска в исследованиях. Это хорошая новость для программистов, потому что это означает, что мы сможем реализовывать более амбициозные проекты. Использование масляной краски, безусловно. оказало такое влияние. Период, непосредственно следующий за ее принятием был золотым веком для живописи. Уже есть признаки того, что что нечто подобное происходит и в программировании.
-------------------------------------------------------------------
Доступно на: http://www.amazon.com/exec/obidos/ASIN/0133708756
Примечания
[1] Маккарти, Джон. Рекурсивные функции символических выражений и их машинное вычисление, часть I. CACM, 3:4 (апрель 1960), pp. 184-195.
Маккарти, Джон. История Lisp. In Wexelblat, Richard L. (Ed.). История языков программирования. Academic Press, New York, 1981, pp. 173-197.
Обе книги были доступны на сайте http://www-formal.stanford.edu/jmc/ на время печати.
[2] Brooks, Frederick P. The Mythical Man-Month. Addison-Wesley, Reading (MA), 1975, p. 16.
Быстрое прототипирование - это не просто способ писать программы быстрее или лучше. Это способ писать программы, которые в противном случае могли бы не быть не были бы написаны вообще.
Даже самые амбициозные люди отказываются от больших начинаний. Легче легче начать что-то, если можно убедить себя (пусть даже убедить себя в том, что это не будет слишком сложной работой. Вот почему так много больших дел начиналось с малого. Быстрое прототипирование позволяет нам начать с малого.
[3] Мюррей, Питер и Линда. The Art of the Renaissance. Thames and Hudson, London, 1963, p. 85.
[4] Янсон, У. Дж. История искусства, 3-е издание. Abrams, New York, 1986, p. 374.
Аналогия применима, конечно, только к картинам, выполненным на панелях а позднее - на холстах. Настенные росписи по-прежнему выполнялись во фрески. Я также не хочу сказать, что стили живописи были обусловлены технологическими изменениями; скорее наоборот.