Код как данные и гомоиконность
Понимаем фундаментальную идею Lisp: программа на Clojure — это просто структура данных.
Гомоиконность — свойство языка, при котором программа записывается теми же структурами данных, которыми она оперирует.
Программа — это список
В прошлых уроках мы видели: (+ 1 2) — это список из символа + и двух чисел. Обычно Clojure такой список вычисляет. Но что если мы хотим посмотреть на код как на данные, не выполняя его? Для этого есть цитирование (quote) — апостроф перед выражением:
(+ 1 2) ; вычисляется => 3
'(+ 1 2) ; цитируется => (+ 1 2), это просто список
; Убедимся, что это настоящий список из трёх элементов
(first '(+ 1 2)) ; => + (символ)
(count '(+ 1 2)) ; => 3Вывод:
3 (+ 1 2) + 3
Манипулируем кодом как данными
Раз код — это список, мы можем строить и менять его обычными функциями для работы со списками. Соберём выражение программно, а потом выполним его через eval:
; Строим список (+ 10 20) из частей
(def выражение (list '+ 10 20))
выражение ; => (+ 10 20)
; eval превращает данные обратно в исполняемый код
(eval выражение) ; => 30Вывод:
(+ 10 20) 30
В большинстве языков так не получится: код там — текст, который нельзя удобно собрать из кусочков. В Lisp код — дерево из списков, и любая функция, работающая со списками, работает и с кодом.
Зачем это нужно
Гомоиконность — фундамент макросов, которым посвящён финальный раздел курса. Макрос — это функция, которая получает невыполненный код как данные, преобразует его и возвращает новый код. Именно так в Clojure можно добавлять новые синтаксические конструкции, не меняя сам язык. Это «суперсила Lisp».
Как работает под капотом
Этап чтения (reading) превращает текст программы в структуры данных Clojure: списки, векторы, символы, числа. Обычно следом идёт компиляция и выполнение. Цитирование (quote или ') останавливает процесс на этапе данных — вы получаете структуру, но не выполняете её. eval же делает обратное: берёт структуру данных и выполняет её как код. Большинство языков не дают доступа к промежуточному дереву; Lisp выставляет его наружу.
Частые ошибки
- Забыть апостроф. Без него
(+ 1 2)сразу вычислится, и вы не получите код как данные. - Злоупотреблять
eval. В реальном кодеevalнужен редко; для расширения языка используют макросы, а неeval. - Думать, что это магия. Никакой магии: просто синтаксис кода совпадает с синтаксисом данных.
Итоги
- Код на Clojure — это структуры данных (списки, символы, числа).
- Апостроф (quote) даёт код как данные, не выполняя его.
- Код можно собирать и менять обычными функциями над списками, а
eval— выполнить. - Гомоиконность — основа макросов, главной суперсилы Lisp.