Learning Clojure/Evaluation

From Wikibooks, open books for an open world
Jump to: navigation, search

Evaluation[edit]

Strings, numbers, characters, nil, true, false, and keywords evaluate to themselves. When the evaluator encounters these things, it simply returns them as is.

When the evaluator encounters a vector or hashmap, it traverses and evaluates the contents before returning the vector or hashmap. Effectively, if a vector or hashmap contains just strings, numbers, characters, nil, true, false, keywords, and other vectors or hashmaps, the vector/hashmap will be returned as is:

["eat my shorts" \space 35.6 true {:fred 22 :alison 8}]      ; will simply be returned by the evaluator

Lists and symbols, however, are evaluated specially:

Symbol evaluation[edit]

The evaluator resolves symbols depending upon the kind of symbol:

A namespace-qualified symbol resolves to the root binding of the Var mapped to the symbol in the specified namespace:

hedgehog/rabbit      ; a symbol resolving to the root binding of the Var mapped to rabbit in the namespace hedgehog

Though a Class may be referred in a namespace, a namespace-qualified symbol will only ever resolve to a Var. If no Var is named by the symbol, an exception is thrown.

A package-qualified symbol (a symbol with . in it) resolves to a Class:

java.util.Arrays     ; a symbol resolving to the Class Arrays in the package java.util

If no such Class can be found, an exception is thrown.

Resolution of a non-qualified symbol is more complicated:

  1. If the symbol is the first item in a list and matches one of the dozen special form names, the list is a special form and evaluated specially (discussed shortly).
  2. If not, the symbol might map to a Class referred in the current namespace.
  3. If not, the symbol might map to a local variable (a local variable is created by special forms, as we'll see).
  4. If not, the symbol might map to a binding of the Var interned or referred in the current namespace. (This may be the root binding or a thread-local binding, as previously discussed.)
  5. If not, the symbol resolves to nothing, and an exception is thrown.

[why must Class lookup be done before locals? shouldn't local names take precedence over class names?]

(Another way of thinking about symbol resolution is that they resolve into Vars, and the Vars in turn evaluate into their bound values. This is more or less correct, but note that namespace-qualified symbols always resolve to the root bound value regardless of the thread context.)

List evaluation[edit]

An empty list is simply evaluated into itself:

()    ; the evaluator returns this as is

A non-empty list handed to the evaluator should start either with a symbol or another list. If a list starts with a symbol:

  1. As stated above, a list beginning with a non-qualified symbol matching a special form name is evaluated specially.
  2. Otherwise, the symbol may resolve to a Var containing a macro (a special kind of function, as we'll see), in which case the remaining elements of the list are left unevaluated and passed to a call to the macro. The value returned by the macro is then substituted in place of the macro call and then evaluated.
  3. Otherwise, the symbol should resolve to a Var containing a function, in which case the remaining elements of the list are evaluated (left-to-right) and then passed to a call to the function. Evaluation of the list returns the value returned by the function.
  4. Otherwise, an exception is thrown.

For example:

(def x 3)                 ; def is a special form, so this list is evaluated specially
(rooster ox (lion 3))     

Assuming rooster resolves to a macro, the macro is called with the arguments of the symbol ox and the list (lion 3). But if rooster resolves to a regular function, the function is called with the arguments of the value resolved from ox and the value returned by evaluation of (lion 3).

Less commonly, a list might start with another list. In this case, the initial list is evaluated and expected to return a macro or function to call (or return a Var bound to a macro or function):

((hamster) "moo")        ; if (hamster) returns a function, that function is called with the argument "moo"