pg.gimran.org

Что отличало Lisp

Декабрь 2001 · Все эссе

Перевод эссе Пола Грэма «Что отличало Lisp». Оригинал: https://paulgraham.com/diff.html. Машинный перевод (Gemini).

Translation of Paul Graham's essay 'What Made Lisp Different'. Original: https://paulgraham.com/diff.html. Machine translation (Gemini).

(ред. май 2002)

(Эта статья появилась в ответ на ряд вопросов в списке рассылки LL1. Сейчас она включена в статью «Месть ботаников».)

Когда Маккарти разработал Lisp в конце 1950-х, это был радикальный отход от существовавших языков, важнейшим из которых был Fortran.

Lisp воплотил в себе девять новых идей:

1. Условные конструкции. Условная конструкция — это структура if-then-else. Сейчас мы воспринимаем их как должное. Они были изобретены Маккарти в процессе разработки Lisp. (В Fortran в то время был только условный goto, тесно связанный с инструкцией ветвления в аппаратном обеспечении того времени.) Маккарти, входивший в комитет Algol, внедрил условные конструкции в Algol, откуда они распространились в большинство других языков.

2. Функциональный тип. В Lisp функции являются объектами первого класса — это такой же тип данных, как целые числа, строки и т. д.; они имеют литеральное представление, могут сохраняться в переменных, передаваться в качестве аргументов и так далее.

3. Рекурсия. Рекурсия существовала как математическая концепция задолго до Lisp, но Lisp стал первым языком программирования, который её поддержал. (Можно утверждать, что это неявно вытекает из превращения функций в объекты первого класса.)

4. Новая концепция переменных. В Lisp все переменные фактически являются указателями. Типы имеют значения, а не переменные, и присваивание или связывание переменных означает копирование указателей, а не того, на что они указывают.

5. Сборка мусора.

6. Программы, состоящие из выражений. Программы на Lisp представляют собой деревья выражений, каждое из которых возвращает значение. (В некоторых диалектах Lisp выражения могут возвращать несколько значений.) Это контрастирует с Fortran и большинством последующих языков, где различаются выражения и инструкции (statements).

Такое различие для Fortran было естественным, поскольку (что неудивительно для языка, форматом ввода которого были перфокарты) язык был ориентирован на строки. Нельзя было вкладывать инструкции друг в друга. И хотя для работы математических вычислений выражения были необходимы, не имело смысла делать так, чтобы что-либо ещё возвращало значение, поскольку ничто не могло его ожидать.

Это ограничение исчезло с появлением блочно-структурированных языков, но к тому моменту было уже слишком поздно. Различие между выражениями и инструкциями укоренилось. Оно перешло из Fortran в Algol, а оттуда — в потомков обоих языков.

Когда язык полностью состоит из выражений, вы можете комбинировать их как угодно. Вы можете написать либо (используя синтаксис Arc):

(if foo (= x 1) (= x 2))

либо

(= x (if foo 1 2))

7. Символьный тип. Символы отличаются от строк тем, что проверять их равенство можно простым сравнением указателей.

8. Нотация для кода с использованием деревьев символов.

9. Постоянная доступность всего языка. Между временем чтения (read-time), временем компиляции (compile-time) и временем выполнения (runtime) нет реальной границы. Вы можете компилировать или выполнять код во время чтения, читать или выполнять код во время компиляции, а также читать или компилировать код во время выполнения.

Выполнение кода во время чтения позволяет пользователям перепрограммировать синтаксис Lisp; выполнение кода во время компиляции лежит в основе макросов; компиляция во время выполнения служит основой использования Lisp как языка расширения в таких программах, как Emacs; а чтение во время выполнения позволяет программам обмениваться данными с помощью s-выражений — идея, которую недавно заново изобрели в виде XML.

Когда Lisp был только создан, все эти идеи были чрезвычайно далеки от обычной практики программирования, во многом диктовавшейся аппаратным обеспечением, доступным в конце 1950-х годов.

Со временем «язык по умолчанию», воплощённый в череде популярных языков, постепенно эволюционировал в сторону Lisp. Пункты 1–5 сейчас получили повсеместное распространение. Пункт 6 начинает проникать в мейнстрим. В Python есть некая форма пункта 7, хотя специального синтаксиса для него, похоже, нет. Пункт 8, который (вместе с 9) и делает возможными макросы Lisp, до сих пор остаётся уникальной чертой Lisp — возможно, потому, что (а) он требует этих скобок или чего-то столь же ужасного, и (б) если добавить этот последний прирост мощи, вы больше не сможете заявлять, что изобрели новый язык, а лишь то, что создали новый диалект Lisp ; -)

Хотя для современных программистов это и полезно, странно описывать Lisp через его отличия от случайных сиюминутных решений, принятых другими языками. Сам Маккарти, вероятно, думал об этом иначе. Lisp создавался не для исправления ошибок Fortran; он возник скорее как побочный продукт попытки аксиоматизировать вычисления.