Thursday, 24 December 2009

Understanding How 'use' Works

As I was working through the "Programming Clojure" book I realized that I didn't fully understand how the 'use' function was working. For example:
(use '[clojure.contrib.repl-utils :only (source)])
Specifically, I didn't understand why the vector of args was quoted i.e. why it started with the inverted comma. So, since I want to understand the language, I decided to work it out.

It turns out that quote is a special form in Clojure: http://clojure.org/special_forms#quote. It yields the unevaluated form, which I take to mean that the evaluator in the REPL just takes the form at face value and defers evaluation. To validate that conclusion I did some experimentation at the REPL prompt:
user=> (def a 2)
#'user/a
user=> [a 1]
[2 1]
user=> '[a 1]
[a 1]
user=> (eval a)
2
Without the quote, the vector [a 1] is evaluated, meaning we get the value of a, which was set to 2. With the quoted vector the 'a' is not evaluated but I can force evaluation by calling eval directly.

So I think the reason that the vector of args to 'use' needs to be quoted is to stop the evaluator evaluating the arguments at the point of entry. Instead they will be evaluated within the context of 'use'....

Hmmm. I don't quite understand the final step yet. I think there is a gap in my knowledge that I need to fill in. Hopefully this will become clearer as I continue to work through the book.

Update 1: I may be a bit closer now. I think the point is that 'use' expects Symbols as the lib names. If I pass in Strings then I get a ClassCastException. The api documentation for 'require' (which is what the api for 'use' refers to) at http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/require says:
Lib names are symbols and each lib is associated
with a Clojure namespace and a Java package that share its name.
 And the way to pass symbols is to quote them to prevent them being evaluated:
user=> (map class '[a b c])
(clojure.lang.Symbol clojure.lang.Symbol clojure.lang.Symbol)
The vector as a whole is called a "Prefix List", defined in the 'require' api as:
It's common for Clojure code to depend on several libs whose names have
the same prefix. When specifying libs, prefix lists can be used to reduce
repetition. A prefix list contains the shared prefix followed by libspecs
with the shared prefix removed from the lib names. After removing the
prefix, the names that remain must not contain any periods.
So in the example at the top of this blog entry, I passed a prefix list to 'use', in the form of a vector. Since the vector was quoted, each item within the vector was quoted, which meant the evaluator did not evaluate the symbols. Hence the symbols were passed as the args, which is what 'use' requires.

I think I understand now.

Update 2: Reading through "ANSI Common Lisp" by Paul Graham I came across a couple of quotes (no pun intended) relevant to this:
Symbols do not (usually) evaluate to themselves, so if you want to refer to a symbol, you should quote it... (ACL, p.11)
 Then, later on the same page:
If a list is quoted, evaluation returns the list itself; if it is not quoted, the list is treated as code, and evaluation returns its value. (ACL, p.11)
 And finally:
Now that we've seen variables, it's easier to understand what symbols are. They are variable names, existing as objects in their own right. And that's why symbols, like lists, have to be quoted. A list has to be quoted because otherwise it will be treated as code; a symbol has to be quoted because otherwise it will be treated as a variable. (ACL, p.15)
The 'use' function expects a vector (sequence?) of symbols, hence the vector needs to be quoted.

No comments:

Post a Comment