«Lisp стоит изучить хотя бы ради того глубокого просветления, которое наступает, когда наконец его понимаешь». — Эрик Реймонд
Racket — язык из одной из старейших и самых необычных семей программирования, где код является данными, скобки — чистой структурой, а программы способны писать программы. Материал знакомит с историей Lisp и постепенно подводит к написанию собственного синтаксиса на Racket.
Немного истории
Lisp родился в 1958 году — его изобрёл Джон Маккарти в MIT. Для сравнения: это второй по возрасту высокоуровневый язык, всё ещё используемый сегодня (только Fortran, появившийся в 1957-м, старше на год). Python появился в 1991-м, JavaScript — в 1995-м. Lisp опережает их более чем на 30 лет, и именно в нём родились идеи, которые сейчас считаются «современными»:
- Сборка мусора — изобретена именно для Lisp.
- Функции первого класса — передача функций как аргументов, сегодня стандарт повсюду.
- REPL — интерактивный цикл «читать-выполнить-напечатать», который сегодня есть в Python, Node и Julia, зародился в Lisp.
- Условия как выражения —
if, возвращающий значение. - Гомоиконичность — код является структурой данных самого языка. Это ключевая идея, к которой материал вернётся в конце.
Десятилетиями Lisp оставался языком искусственного интеллекта. В 70-х и 80-х существовали физические компьютеры, спроектированные для запуска Lisp напрямую — Lisp-машины от Symbolics и LMI. Затем наступила «зима ИИ», финансирование иссякло, и Lisp превратился из звезды в культовый язык.
Но интересные идеи не умирают — они мутируют, и в этом часть красоты лиспов.
От Lisp к Scheme и к Racket
В 1975 году Джеральд Сассман и Гай Стил создали Scheme — минималистичный, элегантный, почти математический диалект Lisp. Scheme стал любимым языком академической среды для обучения программированию (легендарная книга SICP — Structure and Interpretation of Computer Programs — написана именно на Scheme).
В 1995 году группа Матиаса Феллейзена создала PLT Scheme — диалект, разработанный для образования и исследований языков программирования. В 2010 году его переименовали в Racket, и сегодня это куда больше, чем Scheme: это язык для создания языков. Неофициальный девиз — language-oriented programming: если задаче нужен собственный язык, Racket позволяет собрать его за один вечер.
| Год | Событие |
|---|---|
| 1958 | Маккарти изобретает Lisp в MIT |
| 1975 | Сассман и Стил создают Scheme |
| 1984 | Стандартизирован Common Lisp |
| 1995 | Рождается PLT Scheme (будущий Racket) |
| 2007 | Появляется Clojure (Lisp на JVM) |
| 2010 | PLT Scheme переименован в Racket |
| Сегодня | Читатель этой статьи, готовый писать скобки UwU |
Кто использует Lisp сегодня?
Больше людей, чем можно подумать:
- Clojure работает в продакшене банков, авиакомпаний и стартапов (Nubank, крупнейший цифровой банк Латинской Америки, построен на Clojure).
- Common Lisp (с компилятором SBCL) до сих пор применяется в экспертных системах, планировании авиаперелётов (ITA Software, приобретённая Google, лежала в основе Google Flights) и научных вычислениях.
- Emacs Lisp — миллионы людей ежедневно запускают Lisp, даже не подозревая об этом, ведь их редактор сам является интерпретатором Lisp.
- Guile/Guix — целый дистрибутив Linux, настраиваемый на 100% через Scheme.
- Racket имеет собственную ежегодную конференцию (RacketCon), активное академическое и творческое сообщество и применяется для исследования языков, формальной верификации (Rosette), типографики и издательского дела (Pollen), а также в образовании по всему миру.
- Новые лиспы продолжают появляться: Fennel (Lisp, компилирующийся в Lua, популярен в геймдеве), Janet, Hy (Lisp поверх Python)...
Пасхалка для фанатов TADC

В сериале The Amazing Digital Circus (эпизод 8, «hjsakldfhl»), когда Кингер открывает терминал, пытаясь перезагрузить Кейна, видно, что Кейн (креативный ИИ, созданный в 1996 году) написан на Lisp. Файл буквально называется
Caine-core.lisp.
Установка (5 минут)
- Перейти на https://racket-lang.org
- Скачать установщик для своей системы (Linux, macOS, Windows).
- Открыть DrRacket — среду, идущую в комплекте.
В DrRacket две области: сверху пишутся определения (сама программа), а снизу находится REPL для интерактивных экспериментов. На первой строке области определений нужно написать:
#lang racket
Эта строка сообщает Racket, какой язык используется (стоит помнить: Racket — фабрика языков, поэтому нужно выбрать конкретный).
Тем, кто предпочитает терминал: команда racket запускает REPL, а raco — менеджер пакетов и набор инструментов.
Первый контакт: всё является выражением
В REPL можно попробовать:
> (+ 1 2) 3 > (* 3 (+ 2 2)) 12 > (string-append "hello " "world") "hello world"
Правило Lisp умещается в одну строку:
Всё — это
(оператор аргумент1 аргумент2 ...). Всегда. Без исключений.
Не нужно запоминать приоритет операторов, нет особого синтаксиса ни для чего. (+ 1 2) складывает. (if ...) решает. (define ...) называет. Скобки, которые поначалу пугают, на деле означают полное отсутствие произвольных правил. Через неделю их перестаёшь замечать.
Определения и функции
#lang racket (define pi-approx 3.14159) (define (circle-area r) (* pi-approx r r)) (circle-area 2) ; => 12.56636
defineс именем создаёт константу.defineс(имя аргументы...)создаёт функцию.- Комментарии начинаются с
;.
Анонимные функции используют lambda (да, та самая лямбда — лямбда-исчисление Чёрча из 1930-х годов является теоретическим предком всего этого):
(lambda (x) (* x x)) ; функция без имени ((lambda (x) (* x x)) 5) ; => 25, применена сразу
Списки: сердце Lisp
Lisp расшифровывается как LISt Processing — обработка списков. Списки — фундаментальная структура языка:
(list 1 2 3) ; => '(1 2 3) '(1 2 3) ; то же самое, "закавыченное" (first '(1 2 3)) ; => 1 (rest '(1 2 3)) ; => '(2 3) (cons 0 '(1 2 3)) ; => '(0 1 2 3) (length '(a b c)) ; => 3
Обратите внимание на кавычку '. Она говорит Racket: не вычисляй это, это данные. Эта деталь — дверь к финальному трюку.
Функции высшего порядка
Здесь Racket раскрывается по-настоящему. Передача функций другим функциям — самая естественная вещь на свете:
(map (lambda (x) (* x x)) '(1 2 3 4 5)) ; => '(1 4 9 16 25) (filter even? '(1 2 3 4 5 6)) ; => '(2 4 6) (foldl + 0 '(1 2 3 4 5)) ; => 15
map преобразует, filter отбирает, foldl накапливает. С этими тремя функциями можно решить большинство задач со списками, не написав ни одного цикла for.
Рекурсия: мышление по спирали
В Lisp не думают «повторить N раз» — думают «какой базовый случай, и как к нему приблизиться?»:
(define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1))))) (factorial 5) ; => 120
Для наглядности стоит что-нибудь нарисовать — Racket поставляется со встроенными графическими библиотеками:
#lang racket (require 2htdp/image) (define (sierpinski level) (if (= level 0) (triangle 8 "solid" "purple") (let ([t (sierpinski (- level 1))]) (above t (beside t t))))) (sierpinski 6)
Достаточно вставить это в DrRacket, нажать Run — и на экране появится треугольник Серпинского.
Финальный аккорд: код, который пишет код
Вспомним кавычку ' — она превращает код в данные. Смотрите:
'(+ 1 2) ; => СПИСОК (+ 1 2), а не число 3 (first '(+ 1 2)) ; => символ + (eval '(+ 1 2)) ; => 3. Данные только что были выполнены как код.
Программа — это список. Списки можно строить. Значит: можно строить программы с помощью программ. Это и есть гомоиконичность, и именно поэтому в Lisp существуют настоящие макросы — не текстовые макросы, как в C, а функции, которые принимают код и возвращают код ещё до того, как что-либо выполнится.
В Racket нет цикла while? Можно изобрести его:
(define-syntax-rule (while condition body ...) (let loop () (when condition body ... (loop)))) (define counter 0) (while (< counter 5) (displayln counter) (set! counter (+ counter 1)))
Это и есть расширение языка! В Lisp синтаксис принадлежит программисту.
Алан Кэй называл Lisp «уравнениями Максвелла для софта» — крошечным ядром, из которого можно вывести всё остальное.
Что дальше?

- How to Design Programs — книга, вокруг которой был спроектирован Racket, доступна бесплатно онлайн.
- The Racket Guide — официальная документация, одна из лучших в своём роде.
- Beautiful Racket — как создавать собственные языки.
- SICP — классика классик, для тех, кто хочет полного просветления.