Как и в любом другом Лиспе, синтаксис Clojure основан на S-выражениях, которые перед компиляцией транслируются синтаксическим анализатором в структуры данных. Синтаксический анализатор Clojure поддерживает, помимо обычных списков, синтаксис литералов для ассоциативных массивов, множеств и векторов, передавая затем все эти структуры данных компилятору. Иначе говоря, компилятор Clojure компилирует не только списковые структуры данных, но и напрямую поддерживает все указанные типы.
Хотя Clojure является расширением изначальной версии Lisp, он не совместим с Lisp’ом, то есть программа на любой из современных версий Lisp’а (за исключением, возможно, самых коротких, примитивных и, к тому же, специально подобранных примеров) либо вообще не пройдёт транслятор Clojure, либо будет выполняться неправильно. Отличия от распространённых версий Lisp’а приведены на сайте языка[5]. Вот некоторые из них:
утрачена многозначность значения nil (которое в Lisp обозначает и пустой указатель, и пустой список, и логическое значение «ложь») — оно означает только отсутствующее значение (пустую ссылку, подобно null в Java), для прочих значений используется специфический синтаксис;
многие традиционные функции поменяли имена, например, car и cdr заменены на first и rest;
поддерживаются одноимённые функции с различными наборами аргументов;
отсутствуют макросы чтения (read macros), что лишает возможности изменять синтаксис языка;
часть оставшихся неизменными синтаксических элементов изменили смысл;
появилась поддержка «ленивых» коллекций.
Макросы
Макросистема Clojure очень похожа на аналогичную систему Common Lisp, за двумя исключениями:
При раскрытии форм под знаком обратной кавычки ` (англ.back quote), который в Clojure именуется термином «syntax-quote», происходит автоматическая явная квалификация каждого символа тем пространством имён, к которому он относится в точке определения макроса. Такой порядок исключает непреднамеренное связывание с одноимённым символом из «чужого» пространства имён при раскрытии макроса. Сослаться в макросе на символ из другого пространства имён возможно, но только путём его явной квалификации.
Для выделения промежуточных вычислений под знаком обратной кавычки вместо , и ,@ используются ~ и ~@ соответственно.
Особенности языка
Динамическая, интерактивная разработка в REPL-цикле
Тесная интеграция с Java: за счёт компиляции в байткод JVM программы на Clojure легко переносятся в любую среду с JVM. Язык также обеспечивает ряд макросов, которые упрощают использование в нём существующих Java API. Структуры данных Clojure реализуют все стандартные интерфейсы Java, что делает легким запуск из Java программного кода написанного на Clojure.
(let[i(atom0)](defngenerate-unique-id"Возвращает различные числовые ID для каждого вызова."[](swap!iinc)))
Анонимный подкласс java.io.Writer который ничего не выводит и макрос, используемый чтобы заглушить весь вывод внутри него:
(defbit-bucket-writer(proxy[java.io.Writer][](write[buf]nil)(close[]nil)(flush[]nil)))(defmacronoprint"Вычисляет заданные выражения, заглушая весь *вывод* на экран".[&forms]`(binding[*out*bit-bucket-writer]~@forms))(noprint(println"Hello, nobody!"))
10 потоков, манипулирующих одной общей структурой данных, которая состоит из 100 векторов, каждый из которых содержит 10 (изначально последовательных) уникальных чисел. Каждый поток многократно выбирает две случайных позиции в двух случайных векторах и обменивает местами их значения. Все изменения векторов происходят в единой транзакции путём использования системы транзакционной памяти clojure. Поэтому даже после 1000 итераций в каждом из потоков числа не теряются.