Clojure Programming/Tutorials and Tips
Learning material available online
[edit | edit source]Online tutorials
[edit | edit source]- Official Clojure reference & API : link
- "Clojure - Functional Programming for the JVM" by R. Mark Volkmann: link
- Moxley Stratton - "Clojure Tutorial For the Non-Lisp Programmer" : link
- Satish Talim - "Clojure Notes": link
- Eric Rochester - "Clojure Series" (deals with tokenization and stemming) : link
- "Clojure tutorial for ImageJ" : link
- "Wikibooks.org/Clojure Programming/Concepts" : link
Online Books ported to Clojure
[edit | edit source]- Peter Seibel - "Practical Common Lisp" => original ; port to clojure
- Paul Graham - "On Lisp" => original ; chapters 1-5 by fogus link, chapters 7,9,10 by Halloway link, most chapters from 2 - 16 by pangloss on github
- "Structure and Interpretation of Computer Programs" => original ; port to clojure chapter 1 link
Installation Walkthrough
[edit | edit source]- "Clojure, Emacs and Slime/Swank on Ubuntu 8.10" : link
Other Guides
[edit | edit source]- A Brief Beginner's Guide To Clojure (last updated: 2012-03)
Introductions and Tips
[edit | edit source]Clojure for Java Programmers
[edit | edit source]Rich Hickey gave a talk entitled "An Introduction For Java Programmers". Audio and slides are available in two parts on the Clojure blip.tv channel. Good "Clojure Scripting" tutorial here covers many basics of Clojure, as well as its Java integration (for example, using ImageJ from Clojure).
Invoking Clojure from Java
[edit | edit source]The main site's Java Interop reference shows how you can call your Java code from Clojure. But because Clojure is implemented as a Java class library, you can also easily embed Clojure in your Java applications, load code, and call functions.
The main site's Java interop reference also shows how you can do that using the clojure.java.api
package introduced in Clojure 1.6.
Clojure for Scheme Programmers
[edit | edit source]When asked about using SICP to learn about Clojure, Rich had this to say: [26 March 2008]
- Everyone's experience will be different, of course. Here's my 2 cents:
- I don't think SICP is a book about a programming language. It's a book about programming. It uses Scheme because Scheme is in many ways an atomic programming language. Lambda calculus + Tail Call Optimization (TCO) for loops + Continuations for control abstraction + syntactic abstraction (macros) + mutable state for when you need it. It is very small. It is sufficient.
- The book really deals with the issues in programming. Modularity, abstraction, state, data structures, concurrency etc. It provides descriptions and toy implementations of generic dispatch, objects, concurrency, lazy lists, (mutable) data structures, 'tagging' etc, designed to illuminate the issues.
- Clojure is not an atomic programming language. I'm too tired/old/lazy to program with atoms. Clojure provides production implementations of generic dispatch, associative maps, metadata, concurrency infrastructure, persistent data structures, lazy seqs, polymorphic libraries etc etc. Much better implementations of some of the things you would be building by following along with SICP are in Clojure already.
- So the value in SICP would be in helping you understand programming concepts. If you already understand the concepts, Clojure lets you get on with writing interesting and robust programs much more quickly, IMO. And I don't think the core of Clojure is appreciably bigger than Scheme's. What do Schemers think?
- I think the Lisps prior to Clojure lead you towards a good path with functional programming and lists, only to leave you high and dry when it comes to the suite of data structures you need to write real programs, such data structures, when provided, being mutable and imperative. Prior Lisps were also designed before pervasive in-process concurrency, and before the value of high-performance polymorphic dispatch (e.g. virtual functions) as library infrastructure was well understood. Their libraries have decidedly limited polymorphism.
- Stuart Halloway's book Programming Clojure is now available as an e-book (in Beta) and will come out on paper soon. Of course to the extent Schemes go beyond the standard to provide more complete functionality (as most do), there are no books on that either. Just docs in both cases.
- Learning Scheme or Common Lisp on your way to Clojure is fine. There will be specifics that don't translate (from Scheme - no TCO, false/ nil/() differences, no continuations; from CL - Lisp-1, symbol/var dichotomy). But I personally don't think SICP will help you much with Clojure. YMMV.
Function type | Scheme function | Clojure function |
---|---|---|
lists | cons | cons |
list? | seq? | |
car | first | |
cdr | rest | |
caar, cadr, ... | ffirst, fnext, ... |
Clojure for Common Lisp Programmers
[edit | edit source]The table below lists some of the Common Lisp entities (functions, macros etc.) and their (somewhat) equivalent Clojure entities. Note that some of the concepts may not be an exact match. For the exact behavior of the Clojure entities it is recommended that the reader see the Clojure Reference Documentation available at the Clojure home page. Note that some of these differences may be because Clojure has its heritage in Lisp-1 and not Lisp-2.
The Clojure reference also documents common differences to lisps here.
Common Lisp Feature | Clojure Equivalent |
---|---|
load | load-file |
make-array or #(N N N N) | vector or [N N N N] |
#| |# (multiline comment) | (comment ...) |
documentation | doc, find-doc, javadoc |
in-package | in-ns |
defstruct | defstruct and create-struct (defrecord or deftype are preferred in 1.3) |
defun | defn |
inline | definline |
lambda | fn or the reader macro #(..)
|
cons | cons, lazy-cons, conj |
car, first | first |
cdr, rest | rest |
#\x (characters) | \x |
, | ~ |
,@ | ~@ |
eq (and variants) | = |
expt | Math/pow java.lang.Math
user=> (Math/pow 10 2) 100.0 |
format | String/format java.lang.String
user=> (format "0x%x 0x%x 0x%x" 10 20 30) "0xa 0x14 0x1e" or user=> (clojure.pprint/cl-format false "~R" 42) "forty-two" |
gensym | gensym or Simply suffix the symbol name with # as ` has auto-gensyms.
user=> `(x#) (x__2136) |
progn | do |
type-of | class |
typep | instance? |
let | let and binding |
do | loop + recur
(loop [temp-one 1 temp-two 0] (if (= 3 temp-two) temp-one (recur (inc temp-one) (inc temp-one)))) => 3 |
cond | cond, with one level of parens removed
(cond test-form1 form1 test-form2 form2 ... :else ;; :else or non nil or true form) |
Hyperspec |
http://clojure.github.com/clojure/ |
Clojure for Python/Ruby/Perl Programmers
[edit | edit source]A list of equivalent clojure functions for all methods in Ruby's Enumerable and Array classes.
Unit Testing in Clojure
[edit | edit source]There are several unit testing solutions available in Clojure. They have slightly different ideas of how to approach testing. Check out each one and see which fits your testing philosophy the best.
test-is
[edit | edit source]The test-is framework by Stuart Sierra is included in clojure.contrib. It allows you to tag your function definitions with assertions declared by the "is" macro as shown in this example (from the code):
(defn add2 ([x] (+ x 2)) {:test (fn [] (is (= (add2 3) 5)) (is (= (add2 -4) -2) (is (> (add2 50) 50)))}
Tests can be built standalone as well:
(deftest test-new-fn (is (= (new-fn) "Awesome")))
The best place to look for more detail is in the source code itself which is part of the clojure-contrib library on Google Code.
In recent releases, this function has been moved into the core distribution in the namespace clojure.test; a compatibility library still exists in clojure-contrib.
Fact
[edit | edit source]Fact is a unit testing library by James Reeves styled after systems such as Ruby's RSpec. Using this approach, you write your tests as "facts" each with an assertion which should demonstrate that fact:
(fact "The length of a concatenated list is equal to the length of its parts" [xs (rand-seqs rand-ints) ys (rand-seqs rand-ints)] (= (count (concat xs ys)) (+ (count xs) (count ys))))
James posted a description of the library on the google group here. The library is hosted on github here.
unit-test
[edit | edit source]unit-test is a xUnit-style unit test system that allows you to define tests using a deftest macro. Tests contain various assertions and if one of the assertions fails, the test fails.
An example:
(deftest my-example-test [] (let [x 1] (assert-equal 1 x "x is NOT one!")))
The original version of unit-test has not been kept up to date with Clojure changes (original home page here), but Tyler McMullen has patched it up and posted a working version on github here.
Shebang Scripting in Clojure
[edit | edit source]This was tested on Linux only, but should work in other Un*xes.
Put this into command-line-args.clj
#^:shebang '[ exec java -cp "$HOME/.m2/repository/org/clojure/clojure/1.5.1/clojure-1.5.1.jar" clojure.main "$0" "$@" ] (prn *command-line-args*)
Make it executable with something like
$ chmod 755 command-line-args.clj
And then run it with parameters.
$ ~/src/clj/lab/command-line-args.clj a b c ("a" "b" "c")
The explanation for this approach is described in this mail on Clojure group.
A more modern version of this approach is to write command-line-args.clj as:
":";exec java -cp "$HOME/path-to-clojure.jar" clojure.main $0 "$@" (ns command-line-args) (defn command-line? [] (.isAbsolute (java.io.File. *file*))) (defn main [] (println *command-line-args*)) (if (command-line?) (main))
It has a simpler "shebang" line.
(main) is always executed when the script is called from the command-line, even when called with no arguments.
(main) is not executed when command-line-args is used or required by another clojure file.
This method is inspired by this method of emacs scripting and works for the same reasons.
A recent update to Clojure made #! a to-end-of-line comment. Using revision 1106 or later shebang scripting can be done as normal if you have created a clj script, such as those described in Getting started:
#! /usr/bin/env clj (println "Hello World!")
Otherwise, you can reference java the jar directly, like so:
#! /usr/bin/java -jar clojure.jar clojure.lang.Script
Note: this may not work on all systems, since on some systems only one argument is allowed!
On Windows, a similar approach works for embedding a Clojure script into a batch file:
:x (comment @echo off java -cp clojure.jar clojure.main "%~f0" %* goto :eof ) (println "Hi!" *command-line-args*)
Here, the first line is seen as a label (because of the initial colon) by cmd.exe, and as a bare keyword followed by the start of a multi-line comment by Clojure. The next lines (to the closing parenthesis) run Java with an appropriate classpath, passing the batch file name ("%~f0") and the command line arguments ("%*") and then exit the batch file (goto eof). The closing parenthesis then terminates the Clojure comment, and Clojure interprets the rest of the file.
Distributing application as self contained .jar
[edit | edit source](done under linux under bash shell)
- add ./classes and ./ to the CLASSPATH shell variable and make sure that this class-path is also used by clojure (could mean editing the clj bash script):
bash# export CLASSPATH=./classes:./
- bash# mkdir -p project/{app,classes}
- bash# cd project
- create the clojure application app/hello.clj with content:
(ns app.hello (:gen-class)) (refer 'clojure.core) ; not sure if this is necessary (defn -main [& args] (println "application works"))
- compile the clojure application, using clojure REPL/shell:
user=> (compile 'app.hello) app.hello user=> <CTRL><d>
- the compiled application is in classes/app:
bash# ls classes/app/ hello.class hello__init.class hello$_main__4.class
- unpack the clojure.jar into ./classes :
bash# unzip /opt/clojure/clojure.jar -d classes/.
- delete ./classes/META-INF
bash# rm -r ./classes/META-INF
- create manifest text file called mf-app.txt with content:
Main-Class: app.hello Class-Path: .
(make sure that the file ends with a new-line character after the "." character)
- create the .jar file:
bash# jar cmf mf-app.txt app.jar -C classes .
(the command ends with a ".")
- Try the application (app.jar):
bash# java -jar app.jar application works
Using the (ns) macro
[edit | edit source]The (ns) macro can be a bit tricky. Here are some examples that might help.
- Require all of the functions from clojure.contrib.str-utils, without importing them directly into the namespace.
(ns foo (:require clojure.contrib.str-utils)) (clojure.contrib.str-utils/str-join ", " ["foo" "bar"])
- Import all of the functions from clojure.contrib.str-utils directly into the namespace.
(ns foo (:use clojure.contrib.str-utils)) (str-join ", " ["foo" "bar"])
- Import all of the functions from clojure.contrib.str-utils with an aliased namespace.
(ns foo (:require [clojure.contrib [str-utils :as str-utils]])) (str-utils/str-join "," ["foo" "bar"])
- Exclude Clojure's "list" function so that you can define your own function with that name.
(ns foo (:refer-clojure :exclude [list]))