在百度百科中是这么定义引用类型的:“引用类型 由类型的实际值引用(类似于指针)表示的数据类型。如果为某个变量分配一个引用类型,则该变量将引用(或“指向”)原始值。不创建任何副本。引用类型包括类、接口、委托和装箱值类型”。Clojure的引用类型也于此类似。由于在Clojure中,纯函数式的可谓德高望重,备受推崇,也就意味着要尽量少用或者不用那些具有副作用特性的代码。但对数据状态的更改难以避免,毕竟事物总是变化的,因此Clojure中专门定义了一种引用类型。
以下中英文互译内容参考自Clojure - Functional Programming for the JVM。
Reference types are mutable references to immutable data. There are four references types in Clojure: Vars, Refs, Atoms and Agents. They have many things in common:
引用类型是非易变数据的可变引用。Clojure中有四种引用类型: Vars, Refs, Atoms 和 Agents。他们具有一些共同点:
<!--[if !supportLists]-->· <!--[endif]-->They can hold any kind of object.
他们能够持有任意类型的对象。
<!--[if !supportLists]-->· <!--[endif]-->They can be dereferenced to retrieve the object they hold with the deref function or the @ reader macro.
使用deref函数或者@读取器宏能够间接引用他们以获取他们所持有的对象。
<!--[if !supportLists]-->· <!--[endif]-->They support validators which are functions that are invoked when the value changes. If the new value is valid, they return true. Otherwise they either return false or throw an exception. If they simply return false, an IllegalStateException with the message "Invalid reference state" will be thrown.
他们都支持使用一种被称作为验证器的函数,这些函数在值发生改变时调用。如果新的值合法则返回true,否则返回false或者抛出异常。当他们简单地返回false时,将会抛出一个带有“Invalid reference state(非法引用状态)”消息的IllegalStateException异常。
<!--[if !supportLists]-->· <!--[endif]-->They support watchers which are Agents. When the value of a watched reference changes, the Agent is notified. For more detail, see the "Agents" section.
他们都支持一种被称作为监视器的Agents。当被监视的引用的值发生改变时,Agent会被告知。更多细节可以参考章节"Agents"。
The table below summarizes some of the differences between the four reference types and the functions used to create and modify them. Each of the functions in the table below are described later.
下表总结了四种引用类型的不同的和用于创建并更新他们的函数。表中所列的每一个函数都会在接下来的章节中予以阐述。
Var | Ref | Atom | Agent | |
Purpose | synchronous changes 同步更改单个的线程内部的值 | synchronous, coordinated changes 同步并且协调地改变一到多个值 | synchronous changes 同步改变单个值 | asynchronous changes 异步改变单个值 |
To create | (def name initial-value) | (ref initial-value) | (atom initial-value) | (agent initial-value) |
To modify | (def name new-value) (alter-var-root (set! name new-value) | (ref-set ref new-value) (alter ref (commute ref | (reset! atom new-value) (compare-and-set! atom current-value new-value) (swap! atom | (send agent (send-off agent |
Vars
Vars are references that can have a root binding that is shared by all threads and can have a different value in each thread (thread-local).
Vars引用类型可以拥有供所有线程共享的一个root binding,并且可以在每个线程内部持有一个不同的值。
To create a Var and give it a root binding:
Providing a value is optional. If none is given then the Var is said to be "unbound". The same form is used to change the root binding of an existing Var.
值是可选的。如果没有给定值,则Var被当做是为绑定的。可以使用该表达式来改变一个已有Var的root binding,既:(def name value)。
There are two ways to create a thread-local binding for an existing Var:
有两种方式给一个已有Var创建一个线程本地绑定:
(binding [name expression] body)
(set! name expression) ; inside a binding that bound the same name
Use of the binding macro was described earlier. The following example demonstrates using it in conjunction with the set! special form. That changes the thread-local value of a Var that was bound to a thread-local value by the binding macro.
(defn change-it []
(println "2) v =" v) ; -> 1
(def v 2) ; changes root value
(println "3) v =" v) ; -> 2
(binding [v 3] ; binds a thread-local value
(println "4) v =" v) ; -> 3
(set! v 4) ; changes thread-local value
(println "5) v =" v)) ; -> 4
(println "6) v =" v)) ; thread-local value is gone now -> 2
(println "1) v =" v) ; -> 1
(let [thread (Thread. #(change-it))]
(.start thread)
(.join thread)) ; wait for thread to finish
(println "7) v =" v) ; -> 2
The use of Vars is often frowned upon because changes to their values are not coordinated across threads. For example, a thread A could use the root value of a Var and then later discover that another thread B changed that value before thread A finished executing.
使用var往往是令人皱眉, 因为线程之间对于同一个
______________________________________________________________________________
有待完成