Clojure, это представитель семейства Lisp-подобных языков, разработанный
для Java Virtual Machine. Язык идейно гораздо ближе к чистому
[функциональному программированию](https://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5) чем его прародитель Common Lisp, но в то же время обладает набором инструментов для работы с состоянием,
таких как [STM](https://ru.wikipedia.org/wiki/Software_transactional_memory).
Благодаря такому сочетанию технологий в одном языке, разработка программ,
предполагающих конкурентное выполнение, значительно упрощается
и даже может быть автоматизирована.
(Последующие примеры кода предполагают выполнение в Clojure версии 1.2 и выше)
```clojure
; Комментарии начинаются символом ";".
; Код на языке Clojure записывается в виде "форм",
; которые представляют собой обычные списки элементов, разделенных пробелами,
; заключённые в круглые скобки
;
; Clojure Reader (инструмент языка, отвечающий за чтение исходного кода),
; анализируя форму, предполагает, что первым элементом формы (т.е. списка)
; является функция или макрос, который следует вызвать, передав ему
; в качестве аргументов остальные элементы списка-формы.
; Первым вызовом в файле должен быть вызов функции ns,
; которая отвечает за выбор текущего пространства имен (namespace)
(ns learnclojure-ru)
; Несколько простых примеров:
; str объединяет в единую строку все свои аргументы
(str "Hello" " " "World") ; => "Hello World"
; Арифметика тоже выглядит несложно
(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2
; Проверка на равенство (Equality)
(= 1 1) ; => true
(= 2 1) ; => false
; Для булевой логики вам может понадобиться not
(not true) ; => false
; Вложенные формы, конечно же, допустимы и работают вполне предсказуемо
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
; Типы
;;;;;;;;;;;;;
; Clojure использует типы Java для представления булевых значений,
; строк и чисел
; Узнать тип мы можем, использую функцию `class
(class 1) ; Целочисленные литералы типа по-умолчанию являются java.lang.Long
(class 1.) ; Числа с плавающей точкой, это java.lang.Double
(class "") ; Строки всегда заключаются в двойные кавычки
; и представляют собой java.lang.String
(class false) ; Булевы значения, это экземпляры java.lang.Boolean
(class nil) ; "Пустое" значение называется "nil"
; Если Вы захотите создать список из чисел, вы можете просто
; предварить форму списка символом "'", который подскажет Reader`у,
; что эта форма не требует вычисления
'(+ 1 2) ; => (+ 1 2)
; ("'", это краткая запись формы (quote (+ 1 2))
; "Квотированный" список можно вычислить, передав его функции eval
(eval '(+ 1 2)) ; => 3
; Коллекции и Последовательности
;;;;;;;;;;;;;;;;;;;
; Списки (Lists) в clojure структурно представляют собой "связанные списки",
; тогда как Векторы (Vectors), устроены как массивы.
; Векторы и Списки тоже являются классами Java!
(class [1 2 3]); => clojure.lang.PersistentVector
(class '(1 2 3)); => clojure.lang.PersistentList
; Список может быть записан, как (1 2 3), но в этом случае
; он будет воспринят reader`ом, как вызов функции.
; Есть два способа этого избежать:
; '(1 2 3) - квотирование,
; (list 1 2 3) - явное конструирование списка с помощью функции list.
; "Коллекции", это некие наборы данных
; И списки, и векторы являются коллекциями:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true
; "Последовательности" (seqs), это абстракция над наборами данных,
; элементы которых "упакованы" последовательно.
; Списки - последовательности, а вектора - нет.
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false
; Любая seq предоставляет доступ только к началу последовательности данных,
; не предоставляя информацию о её длине.
; При этом последовательности могут быть и бесконечными,
; т.к. являются ленивыми и предоставляют данные только по требованию!