user=> (def x)
#'user/x
user=> x
#object[clojure.lang.Var$Unbound 0x14008db3 "Unbound: #'user/x"]
Clojure 是一种实用的语言,它认识到偶尔需要维护对变化值的持久引用,并提供 4 种不同的机制以受控方式执行此操作 - Var、Ref、Agent 和 Atom。Var 提供了一种机制来引用可变的存储位置,该存储位置可以在每个线程的基础上动态重新绑定(到新的存储位置)。每个 Var 都可以(但不必)具有根绑定,根绑定是所有没有每个线程绑定的线程共享的绑定。因此,Var 的值是其每个线程绑定的值,或者,如果在请求值的线程中未绑定,则为根绑定的值(如果存在)。
特殊形式 def
创建(并 驻留)一个 Var。如果 Var 之前不存在并且没有提供初始值,则该 var 未绑定
user=> (def x)
#'user/x
user=> x
#object[clojure.lang.Var$Unbound 0x14008db3 "Unbound: #'user/x"]
提供初始值会绑定根(即使它已经绑定)。
user=> (def x 1)
#'user/x
user=> x
1
默认情况下,Var 是静态的,但 Var 可以标记为动态的,以便通过宏 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
创建的绑定无法被任何其他线程看到。同样,使用 binding
创建的绑定可以被赋值,这为嵌套上下文提供了一种与代码通信的方法,然后才能将其放置在调用栈上。此功能仅通过将元数据标签:^:dynamic
设置为 true 才能启用,如上面的代码块所示。在某些情况下,可能希望在上下文中重新定义静态 Var,而 Clojure(从 1.3 版本开始)为这些目的提供了函数 with-redefs 和 with-redefs-fn。
使用 defn 定义的函数存储在 Var 中,允许在正在运行的程序中重新定义函数。这也启用了面向方面或面向上下文的编程的许多可能性。例如,您可以在某些调用上下文或线程中仅使用日志记录行为包装函数。
可以通过使用 with-local-vars 创建未驻留的 var。在自由符号解析期间将找不到这些 var,并且必须手动访问它们的值。但它们可以作为有用的线程局部可变单元。
创建 var 的形式 def
、defn
、defmacro
等使用一组标准的 var 元数据 来描述 var。其中一些表单使用显式语法来接受存储在元数据中的值,但通常您也可以将该元数据作为 var 符号上的映射提供。
常见的 var 元数据键(在 var 定义时都可选)
:doc
- 文档化 var 的字符串,通常由文档字符串参数设置
:added
- 文档化添加此 var 的版本的字符串
:private
- 布尔标志,通常由 defn-
设置,供作者声明该 var 是实现细节的意图。私有 var 可全局访问,但在过滤到非私有 var 的 ns-
… 函数中不会被引用或列出。
:arglists
- 参数列表的集合,如果未提供,将自动生成,最常用于记录宏语法
:macro
- 由 defmacro
自动添加的布尔标志(通常不直接使用)
:tag
- var 中值的类型标识符(通常是类)或 var 中保存的函数的返回类型。请注意,var 元数据会被求值,因此 var 上的类型提示(如 ^long
)将求值为 long
函数,而不是 long
原语类型提示。通常,建议改为在 defn var 的参数列表上使用类型提示。
:test
- clojure.test
框架使用此键将单元测试附加到 var(通常不直接使用)
:dynamic
- 表示 var 可以在线程上下文中动态重新绑定(见上文)。使用直接链接进行编译时,动态 var 不会被直接链接。
:redef
- 表示在使用直接链接进行编译时不应直接链接 var(从而允许重新定义它)
:static
- 不再使用(最初 var 默认是动态的,现在默认是静态的)
:const
- 表示 var 是编译时常量,编译器可以将该值内联到使用它的代码中。注意:这很少需要,并且仅适用于编译时的常量(读取,但不求值),例如数字、字符串等(不是类、函数、ref 类型等)。重新定义或动态绑定 const var 不会影响已经编译并加载到运行时的使用该 var 的代码。
有关直接链接和编译期间元数据省略的更多信息,另请参见 编译器选项。
def
的变体:defn defn- definline defmacro defmethod defmulti defonce defstruct
使用驻留的 Var:declare intern binding find-var var
使用 Var 对象:with-local-vars var-get var-set alter-var-root var? with-redefs with-redefs-fn
Var 验证器:set-validator! get-validator
使用 Var 元数据:doc find-doc test