Jump to content

Clojure Programming/FAQ

From Wikibooks, open books for an open world

What would you like to see in the FAQ?

[edit | edit source]

Basics

[edit | edit source]

How do I declare a variable?

[edit | edit source]

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 | edit source]
 (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 | edit source]

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")

How to create a Clojure library locally and use it with Leiningen?

[edit | edit source]

Create a Clojure library

[edit | edit source]

With Leiningen, to create a library, foo:

lein new foo
Generating a project called foo based on the 'default' template.
To see other templates (app, lein plugin, etc), try `lein help new`.

With the default project.clj:

(defproject foo "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]])

Note by Leiningen's terminology, "foo" is name of the artifact, "0.1.0-SNAPSHOT" is the version number.

Go to the root directory of the newly created project, foo. Implement functions. Install the library.

lein install
Created D:\yushen\dev\foo\target\foo-0.1.0-SNAPSHOT.jar
Wrote D:\yushen\dev\foo\pom.xml
Installed jar and pom into local repo.

The above worked with Leiningen 2.4.2.

Use the library

[edit | edit source]

Set up the project, try-foo

lein new try-foo
Generating a project called foo based on the 'default' template.
To see other templates (app, lein plugin, etc), try `lein help new`.

Customize the project.clj file, to express dependency to the library foo

(defproject try-foo "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [foo "0.1.0-SNAPSHOT"]])

then open a repl connected to the project,

(use 'foo.core)

(foo "Hi")

The library should be ready for use.

Gotchas

[edit | edit source]

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

[edit | edit source]

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 | edit source]

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 | edit source]

How do I fetch web pages and make HTTP requests?

[edit | edit source]

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 | edit source]

How can I lookup what methods a Java object contains

[edit | edit source]

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 | edit source]
 (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 | edit source]

Is Clojure object-oriented?

[edit | edit source]

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 | edit source]

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

[edit | edit source]

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

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

[edit | edit source]

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 | edit source]

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

The JVM and Clojure

[edit | edit source]

What versions of Java are supported?

[edit | edit source]

Clojure currently targets Java 5 (and later versions).

What versions of Java have been tested?

[edit | edit source]

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