
Недавно попалось на глаза одно очень интересное видео с динамическими шкалами по популярности разных языков программирования по годам (вроде как, начиная с 1980х). Удивительно, сколько всего и как много придумало человечество. И как быстро меняются тренды, а еще - сколько всего уникального уходит в тень истории. Вот был такой Лисп. Принято считать, что Лисп - это что-то из учебников по истории программирования. Где-то между перфокартами и первыми компиляторами. Условный артефакт. Язык, которым пользовались бородатые профессора, пока не пришел Python и не навел порядок.
Только вот Python тоже унаследовал из Лиспа некоторую часть. Но не все. Самое радикальное так и не забрал.
Откуда взялся Лисп - и почему именно для ИИ
Джон Маккарти был математиком, который всерьез решил, что машины можно научить думать. Не в переносном смысле - технически. И примерно с середины 1950-х он последовательно это доказывал.
Сам термин "artificial intelligence" появился в заявке на Дартмутский летний семинар - в 1955 году. А уже летом 1956-го встреча в Дартмуте фактически закрепила его как название новой научной области. Тогда Маккарти был одним из организаторов. Конечно же, не как маркетинговый слоган - как название дисциплины, под которую он хотел получить финансирование.
Осенью 1958-го Маккарти пришел в MIT. Вместе с Марвином Мински они запустили AI Project - то, что в хронологии MIT обычно датируется 1959-м. MIT AI Lab как отдельная структура выделилась позже, в 1969-м. И практически сразу Маккарти столкнулся с проблемой, которую современный разработчик понял бы мгновенно - нет подходящего инструмента.
FORTRAN создавался для численных расчетов (буквально, “Formula Translation”) и отлично справлялся с вычислениями по формулам. Но задачи искусственного интеллекта требовали работы с символьной информацией. Например - разбора предложений, доказательства теорем, оперирования логическими правилами вида “если А, то Б”. Теоретически любые структуры можно закодировать числами, но на практике описывать сложные вложенные правила в рамках строго числовой абстракции FORTRAN было крайне неудобно.
Для решения этой проблемы инженеры IBM Герберт Гелернтер и Карл Герберих при участии Маккарти разработали FLPL (Fortran List Processing Language) - набор подпрограмм для работы со списками внутри FORTRAN. FLPL помог запустить один из первых ИИ-проектов (геометрический доказыватель теорем), но выявил фундаментальные ограничения исходного языка. В FORTRAN того времени не было ни рекурсии, которая необходима для обработки вложенных списков, ни удобных условных выражений.
Поняв ограничения этого подхода, Маккарти начал проектировать новый язык с нуля. В 1960 году вышла его статья “Рекурсивные функции символьных выражений и их вычисление на машине”, ставшая одной из фундаментальных работ в истории компьютерных наук.
Что Маккарти придумал - и чего он сам не понял
Здесь есть одна примечательная деталь.
Маккарти взял из лямбда-исчисления Алонзо Черча нотацию LAMBDA для обозначения функций. Это стало основой синтаксиса Лиспа. Но сам Маккарти позже признавался, что в момент создания языка не очень-то разбирался в лямбда-исчислении - взял нотацию, потому что она казалась удобной и пошел дальше. Позже Дэвид Парк заметил, что рекурсивную LABEL-нотацию можно выразить через конструкцию на LAMBDA, аналогичную Y-оператору Черча. Но сама LAMBDA в Лиспе Y-комбинатором не является.
Это, кстати, нормальная история для фундаментальных вещей. Автор интуитивно нащупывает правильное решение, а теоретики потом объясняют, почему оно работает.
Главное, что получилось у Маккарти - это идея eval (сокращение от evaluate - “вычислить”). Функция, которая берет программу на Лиспе и выполняет ее. Сегодня это звучит банально. В 1958-м это была концептуальная бомба - программа стала данными, которые можно передавать, трансформировать и запускать.
Человек, который запустил то, что не должно было работать
Маккарти написал eval как математическую нотацию. Он, судя по всему, не планировал реализовывать ее на реальной машине - по крайней мере, не сразу.
Стив Расселл, молодой программист из его группы, прочитал статью и сказал, что может закодировать eval для IBM 704. Маккарти ответил примерно следующее: "Ты путаешь теорию с практикой. Это нотация для чтения, а не для вычислений."
Расселл все равно это сделал. Руками, в машинном коде IBM 704.
Так в 1960-м появился первый интерпретатор Лиспа. Вот просто потому что один человек решил проверить, работает ли теория.
Работала.
Что Лисп придумал первым - и что потом переизобрели все остальные
Итак.
Сборка мусора. Garbage collection - изобретение Лиспа, буквально. Маккарти разработал алгоритм mark-and-sweep (пометка и очистка), чтобы автоматически освобождать память от недостижимых списков. До этого программисты управляли памятью вручную. Java, Python, Go - все взяли эту идею оттуда.
Код как данные. В Лиспе программа и данные записываются одинаково - вложенными списками, S-выражениями. Функцию можно передать как аргумент, вернуть из другой функции, сгенерировать прямо во время выполнения. Это называется гомоиконичностью и именно на этом держатся макросы Лиспа - механизм, который позволяет писать код, генерирующий код.
Рекурсия как основа вычислений. FORTRAN и ранний Кобол работали с итерациями и GOTO. Маккарти показал, что рекурсивные функции - это самодостаточная вычислительная модель.
Условные выражения. if-then-else в том виде, в каком это существует в любом языке - тоже Лисп. Звучит странно, но до 1958-го условные переходы реализовывались как низкоуровневые инструкции, а не как выражения, возвращающие значение.
REPL. Интерактивный read-eval-print-цикл вырос из ранней Лисп-культуры - именно там сложилась привычка работать с живой средой, где пишешь строку и сразу получаешь результат. Сам термин REPL закрепился позже, в документации 1960-х. Jupyter Notebook, Python shell, Node.js repl - все это наследники той же идеи.
Как Лисп стал промышленным стандартом - и что пошло не так
К началу 1960-х Лисп - основной язык ИИ-исследований в MIT, Стэнфорде, Карнеги-Меллоне. К 1980-м на нем работают крупные прикладные системы.
Проблема обнаружилась не в языке. В железе.
Стандартные машины того времени гоняли Лисп медленно - сборка мусора, динамическая типизация, работа со списками требовали ресурсов, которых не хватало. Решение нашли радикальное. Строить специализированные компьютеры под Лисп.
Символы в памяти размечались тегами на уровне архитектуры. Сборка мусора делалась аппаратно. Компании Symbolics и Lisp Machines Inc. (обе - спиноффы MIT AI Lab) выпускали такие машины с конца 1970-х. Texas Instruments тоже вошла в этот рынок.
Цена вопроса - от 150 000 за рабочую станцию. Покупали лаборатории, крупные корпорации, военные.
Во второй половине 1980-х Unix-станции от Sun и других производителей подорвали рынок специализированного железа: стали дешевле и достаточно быстрыми, чтобы использовать Лисп без отдельного процессора. Специализированное железо потеряло экономический смысл.
Symbolics, кстати, пережила это особенно болезненно. Компания подала на банкротство в 1993-м и фактически ушла с рынка в 1990-х. Зато symbolics.com остался историческим артефактом по другой причине - это первый зарегистрированный домен в зоне .com (аж 15 марта 1985 года).
Почему ИИ ушел на Python
В 1980-х символьный ИИ еще переживал бум - на Лиспе и смежных системах строили экспертные системы. Но после “Зимы искусственного интеллекта” конца 1980-х фокус постепенно сместился к статистическому машинному обучению, а в 2010-х окончательно - к глубоким нейросетям. Лисп оказался в очень неудобном положении.
Увы, новые задачи требовали другого. Перемножать матрицы миллиарды раз - это чуть-чуть не про символы и списки. Это про BLAS, CUDA, числа с плавающей точкой в плотных массивах. Python здесь работает как обертка над C++ и CUDA, и делает это хорошо.
Тут, кстати, важный момент. Собственно, Python, с чего мы и начинали эту историю, унаследовал у Лисп - автоматическую память, функции как значения, интерактивную работу, динамические структуры данных. Но самое радикальное, такого как S-выражения или гомоиконичность - Python не взял. Это другая парадигма, и победила та, под которую оказалось больше денег и вычислительных ресурсов.
Но символьное мышление никуда не делось. Современные агентные системы - это в значительной мере управление символами, правилами, контекстом. LLM генерирует код и вызывает инструменты. Это структурно ближе к тому, что делал Лисп, чем к тому, что делает матричное умножение.
Что живет сегодня
Common Lisp, Scheme и Clojure - три основных живых диалекта.
Common Lisp стандартизирован в 1994-м и с тех пор почти не менялся. Это или минус, или плюс - зависит от задачи. Код, написанный тридцать лет назад, работает сегодня без изменений. Немного языков могут этим похвастаться.
Scheme - академический диалект, намеренно минималистичный. Навсегда связан со знаменитым учебником SICP - "Structure and Interpretation of Computer Programs", вышедшим в MIT в 1984-м. В самом MIT этот курс давно уже не является основным вводным, но учебник до сих пор читают как классику - и не только в университетах.
Clojure появился в 2007-м и работает на JVM. Из всех диалектов - самый близкий к промышленному применению. Команды на Clojure встречаются в финтехе и в backend-разработке, где важна иммутабельность данных и конкурентность.
Ни один из них не входит в топ-10 по числу репозиториев на GitHub. Это понятно. Но разработчики, которые их знают, обычно говорят об этом как о сдвиге в способе думать о программировании - не как о добавлении еще одного инструмента в набор.
И еще момент.
Пока это не массовый тренд, а исследовательская гипотеза. Но есть идея в том, что агентные системы, те, которые планируют, рефлексируют и вызывают инструменты - снова делают привлекательными вещи, которые Лисп умел давно. Живое окружение, код как данные, интроспекцию и изменение программы прямо во время выполнения. LLM подключают к живому REPL, агент пишет код и тут же запускает его в той же среде.
Есть конкретные исследовательские работы на эту тему. Хотя и до промышленного масштаба пока далеко, само собой.
Но вот идея, что архитектура шестидесятилетней давности снова оказывается уместной - это, согласитесь, занятно.
