Jump to content

Learning Clojure/Special Forms

From Wikibooks, open books for an open world

Official documentation on special forms in Clojure: http://clojure.org/special_forms#Special%20Forms

In any Lisp dialect, you need special "primitives", called special forms, which are forms evaluated in a special way. For instance, the rules of Clojure evaluation are missing a way to do conditional evaluation, so we have the special form if.

The way arguments to a special form are evaluated is particular to each special form, and these evaluations may change based on context, e.g. the evaluation of special form A may depend on the fact that it is used inside special form B. The Clojure special forms are:

(Arguments to the forms are denoted in italics. Arguments ending ? are optional. Arguments ending * represent 0 or more arguments. Arguments ending + represent 1 or more arguments.)


  • (if test then else?)

If test returns true (any value other than false or nil), then is evaluated and returned. Otherwise, optional else? is evaluated and returned, though if no else? is specified, nil is returned.

(if moose a b)        ; if moose is not false or nil, return a; otherwise, return b
(if (frog) (cow))     ; if (frog) returns something other than false or nil, return value of (cow); otherwise, return nil


  • (quote form)

form is returned unevaluated:

(quote (foo ack bar))    ; returns the list (foo ack bar)
(quote bat)              ; returns the symbol bat itself, not the Var or value resolved from the symbol bat

This is useful when you wish to, say, pass a symbol as an argument to a regular function:

(foo (quote bar))        ; call function foo with argument symbol bar (if foo is a macro, the macro is passed the list (quote bar))
  • (var symbol)

Normally, a symbol resolving to a Var further evaluates into the value of the Var. The special form var returns the Var itself from a resolved symbol, not the Var's value:

(var goose)              ; return the Var mapped to goose

If the symbol does not resolve to a Var, an exception is thrown.


  • (def symbol value)

In the current namespace, symbol is mapped to an interned Var holding value. If a Var mapped to that symbol already exists, def assigns it the new value.

(def george  7)      ; create/set a Var mapped to symbol george in the current namespace and give that Var the value 7 
(def george -3)      ; change the value of that Var to -3

def returns the affected Var.

You can def a namespace-qualified symbol, but only if a Var is already interned by that name in that namespace:

 (def nigeria/fred "hello")  ; change value of Var mapped to nigeria/fred
                             ; throws an exception if the Var mapped to nigeria/fred does not already exist

Attempting to def to a symbol already mapped to a referred Var throws an exception.


  • (fn name? [params*] body*)

Returns a newly defined function object.

name?: the name of the function seen inside the function; useful for recursive calls.

params*: symbols bound to the local parameters.

body*: args to "evaluate" when the function is called; a function call returns the last value returned in its body.

For example:

(fn [] 3)                 ; returns a function which takes no arguments and returns 3
(fn [a b] (+ a b))        ; returns a function which returns the sum of its two arguments
(fn victor [] (victor))   ; returns a function which does nothing but infinitely recursively call itself

Normally, the function object returned by fn is preserved in some way, either passed to a function or bound to a Var or some such. In principle, though, you can immediately call the function returned (not that this is a sensible thing to do):

 ((fn [a b] (+ a b)) 3 5)   ; calls the function with args 3 and 5, returning 8


The symbols provided for name? and params* are not resolved but instead establish local names for the function body. Clojure is lexically scoped, so bindings to local variables in a function take precedence over bindings external to the function, e.g. the symbol foo in a function body will resolve to foo in the current namespace only if there is no local foo and no enclosing function with a local named foo.

When a Clojure function is called, its body is not executed by evaluation as you might think, and in fact, the fn body is partially evaluated before it returns: symbols are resolved and macros are evaluated so that this work is not done each time the function is called; also, special forms in the body are evaluated as best as logic permits, e.g. a fn is evaluated to save from doing the work later, but an if is not evaluated because it represents real work that doesn't make sense to do until the function is actually called. Furthermore, when a function is called, the Clojure evaluator is not really involved because Clojure compiles functions into JVM bytecode. Consider:

(fn [] (frog 5))

When this special form is evaluated, the symbol frog is resolved to a Var in the current namespace. If this Var holds a macro at evaluation time, then the macro call is expanded, but otherwise, the list is compiled into a function call. Assuming frog is not a macro, then when the function we're defining is called and (frog 5) executed, the function held by the Var of frog is called with the argument 5; if the Var does not refer to a function at that time, an exception is thrown.

(Actually, when the evaluator encounters any function call---whether inside a function body or outside---it always compiles it into bytecode; the difference is that calls outside a function definition are immediately executed by the evaluator after compilation.)

Normally, a function has a fixed arity, i.e. it takes a fixed number of arguments. However, the last parameter of a function can be preceded by & to denote that it takes 0 or more extra arguments as a list:

(fn [a b & c] ...)   ; takes 2 or more arguments; all arguments beyond 2 are passed as a list to c
(fn [& x] ...)       ; takes 0 or more arguments; all arguments passed as a list to x

(Normally, & is just a symbol like any other, but it is treated specially by fn for this purpose, so effectively, you can't have a local named &.)

A single function can also be defined with different bodies for different arities using this form:

(fn name? ([params*] body*)+)

For example:

(fn ([] 1)              ; a function which can be called with 0, 1, 2, or 3-or-more arguments
    ([a] 2)             ; returns a different number based on how many args are passed to it
    ([a b] 3)
    ([a b c & d] 4))

In such a function, only one body can have variable arity, and that body's arity must be greater than any other body's.


  • (do body*)

body is any number of arguments which are evaluated in order; the value of the last argument is returned. (We typically don't use do very frequently because a function body is effectively an implicit do that usually meets our needs.)


  • (let [local*] body*)

where local => name value

Declare a local scope in which one or more locals exist:

; a local scope in which aaron is bound to the value 3 
; while bill is bound to the value returned by (moose true)
(let [aaron 3 
      bill (moose true)]
   (print aaron)
   (print bill))

The locals defined are immutable and only visible inside the let.

A local name can be used to define another local later in the list:

(let [mike 6
      kim mike]  ; local kim defined by value of mike
 ;...
)


  • (recur args*)

recur sends execution back to the last "recursion point", which is usually the immediately enclosing function. Unlike a regular recursive call, recur reuses the current stack frame, so it is effectively Clojure's way to do tail-recursion. recur must be used in "tail position" (i.e. as the very last expression possibly evaluated in the function):

(defn factorial [n]
  (defn fac [n acc]
    (if (zero? n)
       acc
      (recur (- n 1) (* acc n)))) ; recursive call to fac, but reuses the stack; n will be (- n 1), and acc will be (* acc n)
  (fac n 1))

In the future, the JVM may add built-in support for tail-call optimization, at which point recur would become unnecessary.


  • (loop [params*] body*)

loop is just like let, except it establishes a "recursion point" for the sake of recur. Effectively, loop is a basic way to do iteration within a function:

(def factorial
  (fn [n]
    (loop [cnt n acc 1]
      (if (zero? cnt)
         acc
        (recur (dec cnt) (* acc cnt))))))  ; send execution back to the enclosing loop with new bindings
                                           ; cnt will be (dec cnt) and acc will be (* acc cnt)
  • (throw expr)

Equivalent of Java's throw.

(throw (rat))        ; throw the exception returned by (rat)
(throw newt)         ; throw the exception named by newt

Just like in Java, a thrown object must be of type java.lang.Throwable or a descendant thereof.


  • (try body* (catch class name body*)* (finally body*)?)

Equivalent of Java's try-catch-finally.

(try
  (bla)
  (bla)
  (catch Antelope x
    (bla x))
  (catch Gorilla y
    (bla y)
  (finally
    (bla)))
  • (monitor-enter)
  • (monitor-exit)

Hickey says, "these are synchronization primitives that should be avoided in user code". You should instead use the macro clojure/locking.


  • (set!)


(Two more special forms, . and new are described in the next section.)

Previous page
Metadata
Learning Clojure Next page
Branching and Monads
Special Forms