user=> (def x)
.'user/x
user=> x
java.lang.IllegalStateException: Var user/x is unbound.
Clojure - прикладной язык программирования, который признает необходимость время от времени держать постоянные ссылки на изменяемые значения и предлагает для этого 4 различных механизма - переменные, ссылки, агенты и атомы. Переменные позволяют ссылаться на изменяемое хранилище, которое может быть динамически заменено (на новое хранилище) для отдельного потока. Каждая переменная может (но не должна) иметь некоторую "главную" связь, которая будет использоваться потоками, не имеющими своей отдельной связи для данной переменной. Таким образом, значение переменной - это значение в хранилище, с которым связана эта переменная в текущем потоке, либо значение в главном хранилище.
Специальная форма def осуществляет ленивое создание переменной. Если эта переменная не существовала и не было предоставлено начального значения - она будет несвязанной:
user=> (def x)
.'user/x
user=> x
java.lang.IllegalStateException: Var user/x is unbound.
Если начальное значение предоставлено - оно помещается в главное хранилище, связанное с переменной (даже если с переменная уже была связанна с некоторым хранилищем).
user=> (def x 1)
.'user/x
user=> x
1
По-умолчанию переменные являются статическими, но они могут быть помечены как динамические чтобы разрешить переопределение связанного хранилища для отдельного потока. Это осуществляется с помощью макроса binding. Внутри каждого потока динамические переменные подчиняются правилам стека:
user=> (def ^:dynamic x 1)
user=> (def ^:dynamic y 1)
user=> (+ x y)
2
user=> (binding [x 2 y 3]
(+ x y))
5
user=> (+ x y)
2
Связи, созданные с помощью binding не будут видны другим потокам. Также эти связи могут быть переопределены, что позволяет вложенным контекстам общаться с кодом, находящимся выше в стеке вызовов. Эта возможность доступна только после того, как для переменной установлен мета-тег dynamic, как в примере выше. Если необходимо переопределить статические переменные в некотором контексте - для этого Clojure (начиная с версии 1.3) предоставляет функции with-redefs и with-redefs-fn.
Функции определенные с помощью defn хранятся в переменных, что позволяет переопределять функции при исполнении программы. Также это позволяет применять подходы аспектно-ориентированного и контекстно-ориентированного программирования. Например, вы можете добавить в функцию журналирование только для некоторых контекстов или потоков.
Специальная форма присвоения.
Если в качестве первого операнда передан символ - ожидается, что он является глобальной переменной. Тогда в текущем потоке для этой переменной будет установлена связь со значением выражения expr. Попытка установить значение в главном хранилище переменной с помощью set! является ошибкой в текущей версии языка. В любом случае возвращается значение выражения expr.
Примечание - вы не можете присвоить значение параметру функции или локальным связям. Только Java-поля, переменные, ссылки и агенты изменяемы в Clojure.
Использование set для Java-полей описано в Java Interop.
Система пространств имен поддерживает глобальные ассоциативные массивы, хранящие соответствие между знаками и переменными-объектами (см. Пространства имен). Если выражение def не находит запись, соответствующую определяемому знаку в текущем пространстве имен, оно создает такую запись. В противном случае def использует существующую переменную-объект. Этот процесс поиска-создания называется ленивым созданием переменной. Это означает, что переменные-объекты являются неизменяемыми ссылками, пока не удалены из пространства имен. Это также означает, что пространства имен составляют глобальное окружение, в котором компилятор пытается разрешить все свободные знаки как переменные (см. Вычисление).
Специальная форма var или маркос считывателя #'
(см. Считыватель) могут быть использованы, чтобы получить переменную-объект, а не её значение.
Можно создавать переменные, которые не будут сохранены в глобальном окружении с помощью with-local-vars. Такие переменные не будут автоматически обрабатываться при разрешении свободных знаков и их значения необходимо будет получать вручную. Тем не менее локальные переменные могут быть использованы как изменяемые ячейки-храниища, принадлежащие отдельному потоку.
Варианты def: defn defn- definline defmacro defmethod defmulti defonce defstruct
Работа с глобальными переменными: declare intern binding find-var var
Работа с переменными-объеками: with-local-vars var-get var-set alter-var-root var? with-redefs with-redefs-fn
Валидаторы переменных: set-validator get-validator
Общие метаданные переменных: doc find-doc test