Clojure Programming/FAQ

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

What would you like to see in the FAQ?[edit]

  • Edit this page and add your questions here!

Basics[edit]

How do I declare a variable?[edit]

As a functional language, Clojure discourages the use of variables in the classic sense of a place that holds a mutable value. If it takes more thought initially to construct solutions that don't need variables, please try to expend the effort - it will repay you many times over. e.g.:

 int i;
 int sum;
 for (i=1;i<=100;i++,sum+=i);

Can be written without variables in Clojure as

 (apply + (range 1 100))

Clojure does support variables but you should read about them fully before using them to understand their semantics. If you are really stuck and want a quick solution, you can use def:

(def a 5)
(def a 6)

But this is not recommended and is only suggested as a workaround until you can more fully explore Clojure.

Clojure also supports variable as a name for an immutable value - meaning you can't change it later. These are created by binding the value to a name, and always have a lexical scope over which the name can be used. Function parameters are one such example, and should be familiar from other languages.

There are a number of clojure constructs that also create temporary bindings (some of them more than once). The simplest is let:

 (let [a 5, b 6]    ; Comma is optional and treated as a whitespace
   (+ a b))
 
 (let [fact100 (factorial 100)]
    (* fact100 fact100))

Given a list of keys and a list of values how do I create a map?[edit]

 (zipmap [:a :b :c] [1 2 3])
 ;; ⇒ {:c 3, :b 2, :a 1}

How can I loop through something and produce a result collection of a different size?[edit]

Suppose I need to process the lines of a text file, and every now and then (based on the line contents) create an object and add it to a list of results. (e.g. a simple parsing of a text file). For example maybe every 5 or 6 lines there is a blank line and I use that to create an object based on the previous lines. Because the number of lines is different I can't use map or anything functional, so it seems. Is with-local-vars required, or is there some more elegant way?


The following code searches through a set of lines and returns matching lines. The def lines simply gives us a collection to work with. The for statement iterates through the "lines" and assigns each entry to "line". For each line, the :when clause is called. If that returns true, then the body of the for is executed, in this case, simply return line. The results are put into a sequence, same as the map function but only containing the matching elements.

 (def lines ["aa" "bb" "cc" "dd"])
 (for [line lines :when (= "bb" line)] line)
 ;; ⇒ ("bb")

The filter function accomplishes a similar thing

 (filter #{"bb"} lines)
 ;; ⇒ ("bb")

Gotchas[edit]

Why doesn't contains? do what I expect on vectors and lists[edit]

Sequential lookup is not an important operation. Clojure includes sets and maps, and if you are going to be looking things up you should be using them. contains? maps to java.util.Set.contains. The fact that java.util.Collection also has contains is, IMO, a mistake, as you really can't write code to an interface with dramatically varying performance characteristics. So, lookups in sets and maps take priority and get the best name - contains? People write naive programs that do linear lookups with contains() in other languages, and get correspondingly bad n-squared performance - that's not an argument for encouraging that in Clojure. If they get an explanation of why it's a bad idea, and how to use sets and maps, that's fine.

Why are some lazy sequences a little eager? (chunked sequences)[edit]

You might have noticed something a little unexpected when using side-effects with some lazy sequences:

 (first (for [i (range 10)] (prn i)))
 ;; => 0 1 2 3 4 5 6 7 8 9

See De-chunkifying sequences in Clojure

Networking[edit]

How do I fetch web pages and make HTTP requests?[edit]

For the simple case of GETing a document into a string you can simply pass a URL to clojure.contrib.duck-streams/slurp*:

(use 'clojure.contrib.duck-streams)
(slurp*  "http://www.example.org/")
;; ⇒ "<HTML>\r\n<HEAD>\r\n  <TITLE>Example Web Page</TITLE>

For more advanced usage, including other request types like POST you may find the clojure-http-client library useful. Finally you can of course use (.openConnection) on a Java URL object directly.

Java interop[edit]

How can I lookup what methods a Java object contains[edit]

You can use the clojure-contrib "show":

(use '[clojure.contrib.repl-utils :only (show)])
(show Object)
;; ⇒
;; ===  public java.lang.Object  ===
;; [ 0] <init> ()
;; [ 1] equals : boolean (Object)
;; [ 2] getClass : Class ()
;; [ 3] hashCode : int ()
;; [ 4] notify : void ()
;; [ 5] notifyAll : void ()
;; [ 6] toString : String ()
;; [ 7] wait : void ()
;; [ 8] wait : void (long)
;; [ 9] wait : void (long,int)

Or if you don't want to use "clojure.contrib" then the following should do some of the work:

 (map #(.getName %) (.getMethods Object))
 ;; ⇒ ("wait" "wait" "wait" "hashCode" "getClass" "equals" "toString" "notify" "notifyAll")

How can I convert a Java HashMap to a Clojure map?[edit]

 (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))
 ;; ⇒ #<HashMap {b=2, a=1}>
 (def hm (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))) 
 hm
 ;; ⇒ #<HashMap {b=2, a=1}>
 (def h (into {} hm))
 h
 ;; ⇒ {"a" 1, "b" 2}
 (h "a")
 ;; 1

Clojure philosophy[edit]

Is Clojure object-oriented?[edit]

Clojure is not object-oriented (at least in the traditional sense). One of the core premises of Clojure is that conflating state and identity in the traditional OO way is a bad idea, particularly in the face of concurrency. On state and identity and Rich Hickey's Are we there yet? talk explain some of the thinking behind this.

Also see Stuart Halloway's Rifle-Oriented Programming article which explains some of Clojure's answers to the concerns of object-oriented programming.

Of course, just because the language doesn't encourage it doesn't mean it is impossible to write object-oriented code in Clojure -- it may be necessary to some extent to inter-operate with Java code. However, traditional OO-style programming is non-idiomatic in Clojure and not encouraged.

Emacs and Clojure[edit]

I've installed paredit.el but it doesn't work for curly braces! {}[edit]

Make sure you're using paredit version 22 (beta)

Where does swank-clojure (SLIME) look for Clojure's jars?[edit]

When you start it with M-x slime, it will add all jars under check ~/.clojure and ~/.swank-clojure to your classpath. You will need at least clojure.jar, clojure-contrib.jar and swank-clojure.jar. You can download pre-built Clojure and Contrib jars from build.clojure.org and swank-clojure.jar from Clojars.

When you use M-x swank-clojure-project and specify a directory swank-clojure will add the following entries to the classpath:

  • src/
  • classes/
  • lib/*.jar

You will need to manually add the clojure, contrib and swank-clojure jar versions you wish to use to the lib directory or use a dependency fetching tool such as Leiningen or Maven.

Slime hangs with the message: Polling "/tmp/slime.####".. (Abort with `M-x slime-abort-connection'.)[edit]

You're probably missing swank-clojure.jar. See the previous question.

The JVM and Clojure[edit]

What versions of Java are supported?[edit]

Clojure currently targets Java 5 (and later versions).

What versions of Java have been tested?[edit]

Clojure 1.0 and 1.1 are both routinely used by many people on various versions of:

  • Sun's JRE 1.5.0 and 1.6.0
  • OpenJDK 1.5.0 and 1.6.0
  • IBM J9

It is also known to run on:

  • JamVM with Classpath