After finding out about Clojure, I just couldn’t wait work on some examples. Step one is getting it downloaded and installed. That’s pretty straightforward, you just download it and follow the examples from the website.
One problem is that running it from the command line is a lot of typing and how do a run code I have saved in a file? Something better will probably come along eventually, but here’s what I did. First, save closure in /usr/local/lib/closure
. Then, copy JLine jar to that directory. Finally, create a shell script at /usr/local/bin/clj
with the following contents:
#!/bin/bash
CLOJURE_DIR=/usr/local/lib/clojure
CLOJURE_JAR=$CLOJURE_DIR/target/clojure-lang-1.0-SNAPSHOT.jar
if [ -z "$1" ]; then
java -cp $CLOJURE_DIR/jline-0.9.93.jar:$CLOJURE_JAR \
jline.ConsoleRunner clojure.lang.Repl
else
java -cp $CLOJURE_JAR clojure.lang.Script $1
fi
So with this, assuming /usr/local/bin
is in your path, you can either run a script of invoke the REPL:
#run foo.clj
clj foo.clj
#start the REPL
clj
Now that we’ve got that out of the way, I thought I would provide a slight variation of the Runtime Polymorphism example:
(defmulti encounter (fn [x y] [(:Species x) (:Species y)]))
(defmethod encounter [:Bunny :Lion] [b l]
(str (b :name) " runs away from " (l :name)))
(defmethod encounter [:Lion :Bunny] [l b]
(str (l :name) " eats " (b :name)))
(defmethod encounter [:Lion :Lion] [l1 l2]
(str (l1 :name) " fights with " (l2 :name)))
(defmethod encounter [:Bunny :Bunny] [b1 b2]
(str (b1 :name) " mates with " (b2 :name)))
(def bugs {:Species :Bunny, :name "Bugs"})
(def betty {:Species :Bunny, :name "Betty"})
(def simba {:Species :Lion, :name "Simba"})
(def scar {:Species :Lion, :name "Scar"})
(println (encounter bugs betty))
(println (encounter bugs simba))
(println (encounter simba bugs))
(println (encounter simba scar))
So refer to the documentation on Runtime Polymorphism for a full explanation of what’s going on here, but here’s my short summary. When you call encounter, the defmulti is called first. The defmulti returns a data structure that is then used to dispatch to a defmethod that matches that data structure. The defmulti uses the value that corresponds to the key :Species
in the map to construct that data structure, but the defmethod doesn’t know or care where that value came from. The first list in the defmethod for encounter is what should be matched. In other words, if the defmulti returns [:foo :bar]
, then it calls defmethod encounter [:foo :bar]
. The second list is the declaration of the arguments that the method takes.
Another little nugget in there is that the comma is whitespace. Maps are defined as a list of alternating key values, surrounded by curly braces. So {:a 1 :b 2 3 :c :d 4}
is a map, but as you can see, sometimes without the commas, it can be confusing. So you can write is as {:a 1, :b 2, 3 :c, :d 4}
to make it more clear where the pairs are, but it’s not required.