On Jan 18, 2009, at 5:11 AM, anand.prabhakar.patil wrote:

I'm new to Java, Lisp and Clojure, so please be patient. I'm trying to make a function that behaves as follows: (my-deref x) returns x if x is a var, or @x if x is a ref. However none of the obvious options seem to be working.


- First, it seems from the api documentation that @x will work fine even if x is a var. I don't understand the following:

user=> (def x 5)
#'user/x
user=> @x
java.lang.IncompatibleClassChangeError (NO_SOURCE_FILE:0)
user=> (var? x)
false

or

user=> (def x)
#'user/x
user=> (var? x)
false

Why isn't x a var?

In all those uses, x is a symbol. The compiler evaluates a symbol by a two step process. First the symbol is "resolved". A symbol has a namespace part (which may be nil) and a name part. Resolution means looking up the var associated with the symbol in: the symbol's namespace if present, else in the current namespace (*ns*), else in a namespace that's "refer"red to by the current namespace. After a symbol is successfully resolved, the compiler knows which var it refers to. The evaluated value of the symbol is the dereferenced value of that var.

In your example, (def x 5) creates a var, makes an entry in the "user" namespace associating that var with the name x (a symbol), and sets that var's value to 5.

When you said @x, the compiler saw the symbol x, looked it up in the current namespace and found the var above, retrieved its value (5), and tried to call "deref" on it. (deref 5) gives "incompatible class change error". (Java 6 gives a better error message in this case: java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IRef (NO_SOURCE_FILE:0))

If you want to refer to a var itself rather than its value, you use the "var" special form. In your case, the var can be retrieved with (var x).

@(var x) returns 5.

#'x is a reader macro that expands to (var x).

There's a good reason that vars don't need "@" to return their value, but instead need var or #' to refer to themselves: this symbol resolution to a var which holds the value mechanism is one of the primary ways we name things in Clojure. If you're dealing with a ref, it's common that you're also dealing with a var that names it (or names some structure that contains it). It's far more common to want the value of a var rather than the var itself.

Here's a table I came up with to explain this all to myself:

              var             ref              agent         atom

object        (var my-var)    my-ref           my-agent      my-atom
constructor   (def my-var {}) (ref {})         (agent {})    (atom {})
value         my-var          @my-ref          @my-agent     @my-atom


(where #'x expands to (var x) and @x expands to (clojure.core/deref x), both of which you can see with macroexpand as in (macroexpand '#'x) and (macroexpand '@x))

We have 4 reference types listed. ref, agent, and atom all act the same, but var is different. In 3 cases the name of the type is a constructor for the type, in the other it's a way to reference the object itself instead of its value. Note that refs agents and atoms are anonymous while one of the primary purposes of vars is to provide storage that can be named by symbols and resolved in namespaces.

The special form "def" doesn't only create a var, it also makes it findable through symbol resolution.

In an expression like:

(def x (ref {}))

We have actually created both a ref and a var that provides a findable place to store it.


- Second, I remember having seen a ref? function analogous to var? in the online documentation, but it doesn't seem to actualy exist:

user=> (ref? x)
java.lang.Exception: Unable to resolve symbol: ref? in this context (NO_SOURCE_FILE:13)

I think there was one in clojure.contrib.pred, but I removed that lib some time ago because most of it had been incorporated into clojure.core. What remained didn't seem sufficiently useful to keep.

Here's one:

user=> (defn ref? [x] (instance? clojure.lang.IRef x))
#'user/ref?
user=> (ref? 3)
false
user=> (ref? (ref {}))
true
user=>

--Steve

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to