Of course, it's probably easier to read Mark Volkmann's intro (particularly in combination with the comparison list against other Lisps). But it's better to learn by doing.
(The REPL session has been edited, partly to remove uninteresting typos (although the typo rate dropped hugely once I found this tip for readline support under MacPorted Clojure) and partly to make it look like I know more Lisp than I actually do.)
Last login: Sun Feb 26 08:00:00 on ttys001
~:clj
Clojure 1.3.0
user=> (+ 1 2)
3
user=> ; aha, looks lispy
user=> ; and the familiar semicolon marks a comment
user=> (+ 2 3 4) ; including end-of-line comments?
9
user=> ; yep, including end-of-line comments
user=> ; so how do I set a variable?
user=> (setf x 1)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: setf in this context, compiling:(NO_SOURCE_PATH)
user=> ; nope, not common-lispy. let's try elispy
user=> (setq x 2)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: setq in this context, compiling:(NO_SOURCE_PATH)
user=> (set x 3)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH)
user=> ; something scheme-y?
user=> (define x 1)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: define in this context, compiling:(NO_SOURCE_PATH)
user=> (set! x 4)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH)
user=> ; (look it up)
user=> (def x 5)
#'user/x
user=>
user=> x
5
user=> ; aha
user=> ; but has Clojure fallen for the original sin of Lisps
user=> ; or is it case-sensitive as God intended?
user=> (def X 15)
#'user/X
user=> x
5
user=> X
15
user=> ; yay
user=> ; how about functions?
user=> (defun (x) (+ x x))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: defun in this context, compiling:(NO_SOURCE_PATH)
user=> ; nope, try something else
user=> (def (x) (+ x x))
CompilerException java.lang.RuntimeException: First argument to def must be a Symbol, compiling:(NO_SOURCE_PATH)
user=> ; interesting.
user=> ; wonder what this 'Symbol' means
user=> ; (look up how to specify the argument list)
user=> (defn [x] (+ x x))
IllegalArgumentException Parameter declaration + should be a vector clojure.core/assert-valid-fdecl (core.clj:6465)
user=> ; can I get help?
user=> (help defn)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: help in this context, compiling:(NO_SOURCE_PATH)
user=> help
CompilerException java.lang.RuntimeException: Unable to resolve symbol: help in this context, compiling:(NO_SOURCE_PATH)
user=> (? defn)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: ? in this context, compiling:(NO_SOURCE_PATH)
user=> ?
CompilerException java.lang.RuntimeException: Unable to resolve symbol: ? in this context, compiling:(NO_SOURCE_PATH)
user=> :?
:?
user=> (doc defn)
-------------------------
clojure.core/defn
([name doc-string? attr-map? [params*] body] [name doc-string? attr-map? ([params*] body) + attr-map?])
Macro
Same as (def name (fn [params* ] exprs*)) or (def
name (fn ([params* ] exprs*)+)) with any doc-string or attrs added
to the var metadata
nil
user=> ; aha!
user=> ; let's try something along these lines
user=> (def f (fn [x] (+ x x)))
#'user/f
user=> ; w00t
user=> (f 2)
4
user=> ; yep, we've got functions
user=> ; but there's clear some sort of macro-driven syntactic sugar we're missing
user=> ; let's try to figure it out
user=> (defn f2 [] ())
#'user/f2
user=> (defn f3 [x] (+ 1 x))
#'user/f3
user=> ; doh
user=> ; I forgot to give it a name the first time round
user=> ; try again
user=> (defn f [x] (+ x x))
#'user/f
user=> (f 2)
4
user=> f
#
user=> x
5
user=> (defn x [x] (+ 3 x))
#'user/x
user=> x
#
user=> (x 2)
5
user=> ; looks like Clojure is a Lisp-1
user=>
user=>
user=>
user=> ; presumably multiple arguments work in an obvious way?
user=> (def my-add2 [x y] (+ x y))
CompilerException java.lang.RuntimeException: Too many arguments to def, compiling:(NO_SOURCE_PATH)
user=> (defn my-add2 [x y] (+ x y))
#'user/my-add2
user=> (my-add 12 23)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: my-add in this context, compiling:(NO_SOURCE_PATH)
user=> (my-add2 12 23)
35
user=> ; yep
user=> ; let's figure out how to get output
user=> ; because that's going to be very useful in our experiments
user=> (format t "hello world")
CompilerException java.lang.RuntimeException: Unable to resolve symbol: t in this context, compiling:(NO_SOURCE_PATH)
user=> ; well it doesn't look to be format
user=> ; but that error message is interesting
user=> ; we might not be in traditional lisp-land
user=> t
CompilerException java.lang.RuntimeException: Unable to resolve symbol: t in this context, compiling:(NO_SOURCE_PATH)
user=> nil
nil
user=> ; well, at least some of the landmarks are familiar
user=> true
true
user=> (not nil)
true
user=> ; aha
user=> ; looks like true is t in clojure
user=> ; back to printing things out
user=> (format true "hello world")
ClassCastException java.lang.Boolean cannot be cast to java.lang.String clojure.core/format (core.clj:5045)
user=> ; another error message with a hint
user=> (format "hello world")
"hello world"
user=> ; aha edit: actually, not so much
user=> ; how about parameters?
user=> (format "hello ~a" "world")
"hello ~a"
user=> ; oh well
user=> ; mind you, an observation: strings are double-quote delimited as expected
user=> ; let's try C-style
user=> (format "hello %s" "world")
"hello world"
user=> ; kewl
user=> ;
user=> ; now let's hunt for some control structures
user=> ; iirc, doc looked useful
user=> ; let's try it on a few candidates
user=> (doc if)
-------------------------
if
(if test then else?)
Special Form
Evaluates test. If not the singular values nil or false,
evaluates and yields then, otherwise, evaluates and yields else. If
else is not supplied it defaults to nil.
Please see http://clojure.org/special_forms#if
nil
user=> (doc case)
-------------------------
clojure.core/case
([e & clauses])
Macro
Takes an expression, and a set of clauses.
Each clause can take the form of either:
test-constant result-expr
(test-constant1 ... test-constantN) result-expr
The test-constants are not evaluated. They must be compile-time
literals, and need not be quoted. If the expression is equal to a
test-constant, the corresponding result-expr is returned. A single
default expression can follow the clauses, and its value will be
returned if no clause matches. If no default expression is provided
and no clause matches, an IllegalArgumentException is thrown.
Unlike cond and condp, case does a constant-time dispatch, the
clauses are not considered sequentially. All manner of constant
expressions are acceptable in case, including numbers, strings,
symbols, keywords, and (Clojure) composites thereof. Note that since
lists are used to group multiple constants that map to the same
expression, a vector can be used to match a list if needed. The
test-constants need not be all of the same type.
nil
user=> (doc while)
-------------------------
clojure.core/while
([test & body])
Macro
Repeatedly executes body while test expression is true. Presumes
some side-effect will cause test to become false/nil. Returns nil
nil
user=> (doc let)
-------------------------
clojure.core/let
(let [bindings*] exprs*)
Special Form
binding => binding-form init-expr
Evaluates the exprs in a lexical context in which the symbols in
the binding-forms are bound to their respective init-exprs or parts
therein.
Please see http://clojure.org/special_forms#let
nil
user=> (doc progn)
nil
user=> (doc labels)
nil
user=> ; let's try a few
user=> x
#
user=> (def x 1)
#'user/x
user=> ; hang on a minute
user=> ; before we do any control structures
user=> ; we need to explore equality
user=> ; do we have the panoply of different concepts of equality that common lisp has?
user=> (def y 1)
#'user/y
user=> (def z 1.0)
#'user/z
user=> x
1
user=> y
1
user=>
user=> z
1.0
user=> (equals x x)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: equals in this context, compiling:(NO_SOURCE_PATH)
user=> (equal x x)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: equal in this context, compiling:(NO_SOURCE_PATH)
user=> (eql x x)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: eql in this context, compiling:(NO_SOURCE_PATH)
user=> (= x x)
true
user=> (== x x)
true
user=> (= x y)
true
user=> (= x z)
false
user=> ; OK, there's no automatic conversion between floats and ints
user=> ; good enough for now
user=> ; I think we need to come back to this for a deeper look later
user=> ; when we've found out about atoms and non-atoms etc
user=> x
1
user=> (if (= 1 x) (format "x is one"))
"x is one"
user=> (if (= 2 x) (format "x is two") (format "x isn't two too"))
"x isn't two too"
user=> ; hang on though
user=> (doc if)
-------------------------
if
(if test then else?)
Special Form
Evaluates test. If not the singular values nil or false,
evaluates and yields then, otherwise, evaluates and yields else. If
else is not supplied it defaults to nil.
Please see http://clojure.org/special_forms#if
nil
user=> ; presumably we need exactly 2 or 3 arguments to if
user=> (if (= 2 x) (format "x is two") (format "x isn't two") (format "something else")
)
CompilerException java.lang.RuntimeException: Too many arguments to if, compiling:(NO_SOURCE_PATH)
user=> ; ok, just as we thought
user=>
user=>
user=>
user=> ; so we've got enough for a Lisp-style hello world
user=> (defn factorial [n] (if (= n 0) 1 (factorial (- n 1))))
#'user/factorial
user=> (factorial 1)
1
user=> (factorial 2)
1
user=> (factorial 3)
1
user=> ; oops missed a key step
user=>
user=>
user=> (defn factorial [n] (if (= n 0) 1 (factorial (- n 1))))
#'user/factorial
user=> (defn factorial [n] (if (= n 0) 1 (* n (factorial (- n 1)))))
#'user/factorial
user=> (factorial 3)
6
user=> (factorial 4)
24
user=> ; that's more like it
user=> ; and it's recursive, so we feel we're properly in Lisp land
user=>
user=> ; how about some binding creation stuff
user=> (let (c 1) (format "hello"))
IllegalArgumentException let requires a vector for its binding clojure.core/let (core.clj:3962)
user=> ; it's talking about 'vectors' again
user=> ; that needed square brackets before
user=> (let [c 1] (format "hello"))
"hello"
user=> (let [c 1] (format "hello number ~a" c))
"hello number ~a"
user=> ; drat, forgot the format specifier
user=> (let [c 1] (format "hello number %s" c))
"hello number 1"
user=> ; how about multiple bindings?
user=> (let [c 1 d 2] (format "hello number %s" c))
"hello number 1"
user=> (let [c 1 d 2] (format "this format was brought to you by the numbers %s and %s" c d))
"this format was brought to you by the numbers 1 and 2"
user=> ; how about let* ?
user=> (let [c 1 d (+ c 2)] (format "this format was brought to you by the numbers %s and %s" c d))
"this format was brought to you by the numbers 1 and 3"
user=> (let [c 1 d (+ c 2) c 5] (format "this format was brought to you by the numbers %s and %s" c d))
"this format was brought to you by the numbers 5 and 3"
user=> (let [d (+ c 2) c 5] (format "this format was brought to you by the numbers %s and %s" c d))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: c in this context, compiling:(NO_SOURCE_PATH)
user=> ; so let seems to be like LET*
~:clj
Clojure 1.3.0
user=> ; let's have a look at the most basic bricks for data
user=> ; we've already seen a couple of numeric types
user=> 1
1
user=> 1.0
1.0
user=> (= 1 1.0)
false
user=> true
true
user=> (= false true)
false
user=> ; well, that's reassuring
user=> (type 1)
java.lang.Long
user=> ; ooh
user=> ; now that's interesting
user=> ; a) we've got a type thingy
user=> ; b) the base type comes from Java
user=> ;
user=> ; what type is a type?
user=> (doc type)
-------------------------
clojure.core/type
([x])
Returns the :type metadata of x, or its Class if none
nil
user=> (type (type 1))
java.lang.Class
user=> ; hmm
user=> ; that has a sniff of interesting possibilities
user=> ; ... to come back to
user=>
user=> ;
user=> ; back to b)
user=> ; how many of the Java POD types can we get at?
user=> 'a'
a'
user=> ; ah, oops, been writing too much Python
user=> ; of course single quotes don't enclose things
user=> ; we're in Lisp-land
user=> ; try again
user=> "a"
"a"
user=> (type "a")
java.lang.String
user=> (type 1.0)
java.lang.Double
user=> ; interesting to note that it's calling this
user=> ; the wrapper type 'java.lang.Double' not the primitive type double
user=> float
#
user=> (float 1.0)
1.0
user=> (def x (float 1.0))
#'user/x
user=> c
CompilerException java.lang.RuntimeException: Unable to resolve symbol: c in this context, compiling:(NO_SOURCE_PATH)
user=> x
1.0
user=> (type x)
java.lang.Float
user=> ; so it's defaulting to Double but you can have less precision if you want
user=> ; how about integral types?
user=> (int 1)
1
user=> (type (int 1))
java.lang.Long
user=> (type (short 1))
java.lang.Short
user=> (type false)
java.lang.Boolean
user=> (type (byte 1))
java.lang.Byte
user=> ; that mostly makes sense
user=> ; not sure why int is missing
user=> ; let's check the range
user=> (def bb (byte 255))
IllegalArgumentException Value out of range for byte: 255 clojure.lang.RT.byteCast (RT.java:997)
user=> ; aha, -128 to 127 like Java I'm guessing
user=> ; (although why on earth the Java developers thought that was a good idea for a byte type is beyond me)user=> (def bb (byte 127))
#'user/bb
user=> (def bb (byte 128))
IllegalArgumentException Value out of range for byte: 128 clojure.lang.RT.byteCast (RT.java:997)
user=> bb
127
user=> (+ bb 1)
128
user=> (type (+ bb 1))
java.lang.Long
user=> ; OK, looks like either promotion on mixed types
user=> ; or promotion on overflow
user=> ; let's figure out which
user=> (type (+ (byte 1) (byte 2)))
java.lang.Long
user=> (def x (byte 1))
#'user/x
user=> (def y (byte 2))
#'user/y
user=> (type x y)
ArityException Wrong number of args (2) passed to: core$type clojure.lang.AFn.throwArity (AFn.java:437)
user=> (mapcar type '(x y))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: mapcar in this context, compiling:(NO_SOURCE_PATH)
user=> ; OK, maybe we'll come back to that
user=> (type x)
java.lang.Byte
user=> (type y)
java.lang.Byte
user=> (+ x y)
3
user=> (def z (+ x y))
#'user/z
user=> (type z)
java.lang.Long
user=> ; for bytes, looks like promotion on arithmetic operation
user=> (def x (short 1))
#'user/x
user=> (def y (short 2))
#'user/y
user=> (def z (+ x y))
#'user/z
user=> (type z)
java.lang.Long
user=> ; same for shorts
user=> ; would be a bit disappointing at the beach though
user=>
user=>
user=> ; if your pair of shorts got automatically converted to longs
user=>
user=>
user=> ; moving swiftly on
user=> (char 'a')
ClassCastException clojure.lang.Symbol cannot be cast to java.lang.Number clojure.lang.RT.charCast (RT.java:905)
user=> (char 32)
\space
user=> (type (char 32))
java.lang.Character
user=> ; given we've got a Java substratum, I'm guessing that...
user=> (def a_macron (char 256))
#'user/a_macron
user=> (type a_macron)
java.lang.Character
user=> a_macron
\?
user=> (format a_macron)
ClassCastException java.lang.Character cannot be cast to java.lang.String clojure.core/format (core.clj:5045)
user=> (format "%s" a_macron)
"?"
user=> ; hmm, dunno if that's just a missing font/encoding
user=> ord
CompilerException java.lang.RuntimeException: Unable to resolve symbol: ord in this context, compiling:(NO_SOURCE_PATH)
user=> (doc (type a_macron))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3879)
user=> (doc java.lang.Character)
CompilerException java.lang.RuntimeException: Unable to resolve var: java.lang.Character in this context, compiling:(NO_SOURCE_PATH)
user=> ; digression {
user=> ; can we specify numbers in other bases?
user=> 0x1
1
user=> 0xF
15
user=> ; kewl
user=> ; makes it easier to...
user=> ; } undigress
user=> ; get outside the BMP
user=> (char 0x10100)
IllegalArgumentException Value out of range for char: 65792 clojure.lang.RT.charCast (RT.java:940)
user=> ; or not
user=> ; (that should have been AEGEAN WORD SEPARATOR LINE)
user=> ; (for anyone who cares)
user=>
user=> ; strings look to be fairly built-in
user=> (def hw "hello world")
#'user/hw
user=> hw
"hello world"
user=> (type hw)
java.lang.String
user=> ; but do they count as a compound type of some form?
user=> (first hw)
\h
user=> (type (first hw))
java.lang.Character
user=> ; so a string can be broken down into characters
user=> (string "abc")
CompilerException java.lang.RuntimeException: Unable to resolve symbol: string in this context, compiling:(NO_SOURCE_PATH)
user=> (doc string)
nil
user=> (str (char 32) (char 34))
" \""
user=> (type (str (char 32) (char 34)))
java.lang.String
user=> ; and we can build characters back up into strings
user=>
user=> ; of course, this wouldn't be Lisp
user=> ; without lists
user=> (list 1 2 3)
(1 2 3)
user=> ; how about the normal syntactic sugar?
user=> '(1 2 3)
(1 2 3)
user=> (def x (list 1 2 3))
#'user/x
user=> (type x)
clojure.lang.PersistentList
user=> ; hmm
user=> ; I wonder if there's a non-Persistent list?
user=> ;
user=> ; let's try to be a bit trad
user=> (cons 1 2)
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:487)
user=> x
(1 2 3)
user=> (car x)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: car in this context, compiling:(NO_SOURCE_PATH)
user=> ; these young whippersnappers
user=> ; no respect for tradition
user=> (cdr x)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: cdr in this context, compiling:(NO_SOURCE_PATH)
user=> (first x)
1
user=> (rest x)
(2 3)
user=> (1 . 2)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: . in this context, compiling:(NO_SOURCE_PATH)
user=> ; interesting
user=> ; no cons cells
user=> ; there must be some other way of doing the key data structures
user=> ; that traditionally use cons cells directly
user=>
user=> ; now, we've seen some other sequence types
user=> (setq ss "abcde")
CompilerException java.lang.RuntimeException: Unable to resolve symbol: setq in this context, compiling:(NO_SOURCE_PATH)
user=> ; drat, forgot again
user=> (def ss "abcde")
#'user/ss
user=> ; lets set up a little utility function
user=> (defn printall [x] (if x (do (format "%s" (first x)) (printall (rest x)))))
#'user/printall
user=> ; (I cheated a bit, and looked up that PROGN is 'do' in Clojure)
user=> (printall ss)
StackOverflowError java.util.regex.Pattern$CharProperty.match (Pattern.java:3343)
user=> (def y '())
#'user/y
user=> (if y (format "empty list evaluates to true") (format "empty list is falsy"))
"empty list evaluates to true"
user=> (listp y)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: listp in this context, compiling:(NO_SOURCE_PATH)
user=> (list? y)
true
user=> ; aha, Scheme-style. Nice
user=> (nil? y)
false
user=> (empty? y)
true
user=> (empty? x)
false
user=> ; let's try again
user=> (defn printall [x] (if (empty? x) () (do (format "%s" (first x)) (printall (rest x)))))
#'user/printall
user=> (printall x)
()
user=> (printall ss)
()
user=> (not true)
false
user=> ; Ok, that lets us make it neater...
user=> (defn printall [x] (if (not (empty? x)) (do (format "%s" (first x)) (printall (rest x)))))
#'user/printall
user=> (printall ss)
nil
user=> ; ...but no more functional :-(
user=> (defn temp [] (format "dasdf"))
#'user/temp
user=> (temp)
"dasdf"
user=> (def xyzzy (temp))
#'user/xyzzy
user=> ; OK, so output doesn't appear
user=> ; looks like I misunderstood format
user=> ; it's not an output function like Common Lisp
user=> ; let's try something more Java-esque
user=> (print "hello")
hellonil
user=> (println "hello")
hello
nil
user=> ; aha
user=> (defn printall [x] (if (not (empty? x)) (do (println (first x)) (printall (rest x)))))
#'user/printall
user=> (printall ss)
a
b
c
d
e
nil
user=> ; phew
user=> (printall x)
1
2
3
nil
user=> ; how about those square brackets in argument lists
user=> ; there was muttering about 'vectors'
user=> (def vv [1 2 3])
#'user/vv
user=> (type vv)
clojure.lang.PersistentVector
user=> (printall vv)
1
2
3
nil
user=> ; so first and rest work on vectors OK
user=> (doc time)
-------------------------
clojure.core/time
([expr])
Macro
Evaluates expr and prints the time it took. Returns the value of
expr.
nil
user=> (doc vec)
-------------------------
clojure.core/vec
([coll])
Creates a new vector containing the contents of coll.
nil
user=> (vec (range 10))
[0 1 2 3 4 5 6 7 8 9]
user=> (list (range 10))
((0 1 2 3 4 5 6 7 8 9))
user=> (flatten (list (range 10)))
(0 1 2 3 4 5 6 7 8 9)
user=> ; OK, we can build some lists and vectors
user=> ; big 'uns
user=> (def longv (vec (range 10000)))
#'user/longv
user=> (def longl (flatten (list (range 10000))))
#'user/longl
user=> (nth x 10)
10
user=> (nth longv 9999)
9999
user=> (nth longl 9999)
9999
user=> (time (nth longl 9999))
"Elapsed time: 0.834 msecs"
9999
user=> (time (nth longv 9999))
"Elapsed time: 0.034 msecs"
9999
user=> ; as expected, random list access is probably O(N)
user=> ; whereas vector is more O(1)-ish
user=>
user=> ; OK, so far we've got a list
user=> (list 1 2 3)
(1 2 3)
user=> ; with the usual syntactic sugar
user=> '(4 5 6)
(4 5 6)
user=> (type '(4 5 6))
clojure.lang.PersistentList
user=> (type (list 4 5 6))
clojure.lang.PersistentList
user=> ; and a vector
user=> (type (vector 1 2 3))
clojure.lang.PersistentVector
user=> ; with a new grain of syntactic sugar, namely square brackets
user=> (type [4 5 6])
clojure.lang.PersistentVector
user=> ; We've also found that lists aren't built from conses
user=> (1 . 2)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: . in this context, compiling:(NO_SOURCE_PATH)
user=>
user=> ; so what else is there?
user=> ; a quick sneaky peek at the docs
user=> ; talks about maps and sets as other collection types
user=> ; so let's explore
user=>
user=> (doc map)
-------------------------
clojure.core/map
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments.
nil
user=> ; OK, that's a mapcar-type thingy
user=> (doc map?)
-------------------------
clojure.core/map?
([x])
Return true if x implements IPersistentMap
nil
user=> ; cheat and look up the fact there are two sorts of map
user=> (doc hash-map)
-------------------------
clojure.core/hash-map
([] [& keyvals])
keyval => key val
Returns a new hash map with supplied mappings.
nil
user=> (doc sorted-map)
-------------------------
clojure.core/sorted-map
([& keyvals])
keyval => key val
Returns a new sorted map with supplied mappings.
nil
user=> (hash-map :a 1 :b 2)
{:a 1, :b 2}
user=> (def hm (hash-map :a 1 :b 2))
#'user/hm
user=> (type hm)
clojure.lang.PersistentHashMap
user=> (hm :a)
1
user=> (hm :b)
2
user=> (hm :c)
nil
user=> ; so looking up a value is straightforward: the map object acts like a lookup function
user=> ; how about testing?
user=> (:c in hm)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: in in this context, compiling:(NO_SOURCE_PATH)
user=> (in :c hm)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: in in this context, compiling:(NO_SOURCE_PATH)
user=> (in? :c hm)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: in? in this context, compiling:(NO_SOURCE_PATH)
user=> (doc hm)
-------------------------
user/hm
nil
nil
user=> (doc hash-map)
-------------------------
clojure.core/hash-map
([] [& keyvals])
keyval => key val
Returns a new hash map with supplied mappings.
nil
user=> (values hm)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: values in this context, compiling:(NO_SOURCE_PATH)
user=> (vals hm)
(1 2)
user=> ; aha
user=> ; that leads to a logical guess:
user=> (keys hm)
(:a :b)
user=> (type (keys hm))
clojure.lang.APersistentMap$KeySeq
user=> (count hm)
2
user=> (len hm)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: len in this context, compiling:(NO_SOURCE_PATH)
user=> hm
{:a 1, :b 2}
user=> (id hm)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: id in this context, compiling:(NO_SOURCE_PATH)
user=> (first hm)
[:a 1]
user=> (nth hm 3)
UnsupportedOperationException nth not supported on this type: PersistentHashMap clojure.lang.RT.nthFrom (RT.java:776)
user=> ; how about modifying the map?
user=> ; the sneak peak earlier mentioned 'assoc' and 'dissoc'
user=> ; so let's have a go
user=> ;
user=> ; but before we start, let's save off our original hash-map
user=> (def ohm hm)
#'user/ohm
user=> ohm
{:a 1, :b 2}
user=> (doc assoc)
-------------------------
clojure.core/assoc
([map key val] [map key val & kvs])
assoc[iate]. When applied to a map, returns a new map of the
same (hashed/sorted) type, that contains the mapping of key(s) to
val(s). When applied to a vector, returns a new vector that
contains val at index. Note - index must be <= (count vector).
nil
user=> (assoc hm :d 4)
{:a 1, :b 2, :d 4}
user=> hm
{:a 1, :b 2}
user=> ; interesting
user=> ; the original hash-map is unchanged, but the assoc operation returns a new one
user=> (def newhm (assoc hm :d 4))
#'user/newhm
user=> newhm
{:a 1, :b 2, :d 4}
user=> hm
{:a 1, :b 2}
user=> ohm
{:a 1, :b 2}
user=> (== ohm hm)
ClassCastException clojure.lang.PersistentHashMap cannot be cast to java.lang.Number clojure.lang.Numbers.equiv (Numbers.java:206)
user=> ; interesting digression -- looks like == is just for numbers
user=> (= ohm hm)
true
user=> (= ohm newhm)
false
user=> (doc dissoc)
-------------------------
clojure.core/dissoc
([map] [map key] [map key & ks])
dissoc[iate]. Returns a new map of the same (hashed/sorted) type,
that does not contain a mapping for key(s).
nil
user=> (def newhm (dissoc :a))
#'user/newhm
user=> newhm
:a
user=> (doc set)
-------------------------
clojure.core/set
([coll])
Returns a set of the distinct elements of coll.
nil
user=> (set 1 2 3 4 3 2 1)
ArityException Wrong number of args (7) passed to: core$set clojure.lang.AFn.throwArity (AFn.java:437)
user=> (set [1 2 3 4 3 2 1])
#{1 2 3 4}
user=> (def ss (set [1 2 3 4 3 2 1]))
#'user/ss
user=> (keys ss)
ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry clojure.lang.APersistentMap$KeySeq.first (APersistentMap.java:132)
(user=>
user=> (vals ss)
ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry clojure.lang.APersistentMap$ValSeq.first (APersistentMap.java:163)
(user=> (ss 1)
1
user=> (ss 9)
nil
user=> (ss 2)
2
user=> ; ok, treated as a function, (set x) returns x if it's in the set, nil otherwise
user=> (def ss_with_nil (set '(1 2 nil 4)))
#'user/ss_with_nil
user=> ss_with_nil
#{nil 1 2 4}
user=> (ss nil)
nil
user=> ; hmm, there must be some other way to test membership
user=> (len ss)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: len in this context, compiling:(NO_SOURCE_PATH)
user=> (count ss)
4
user=> (contains? ss nil)
false
user=> (contains? ss_with_nil nil)
true
user=> ; aha
user=> (assoc ss 8)
ArityException Wrong number of args (2) passed to: core$assoc clojure.lang.AFn.throwArity (AFn.java:437)
user=> ss
#{1 2 3 4}
user=> ; looks like add/remove are conj/disj
user=> (conj ss 7)
#{1 2 3 4 7}
user=> (disj ss 2)
#{1 3 4}
user=> ; but again these return a new set
user=> (def ss_with_7 (conj ss 7))
#'user/ss_with_7
user=> (= ss ss_with_7)
false
user=> ss
#{1 2 3 4}
user=> ss_with_7
#{1 2 3 4 7}
user=> ;
user=> ;
user=> ; pour some sugar on me
user=> ; with both hash-maps and sets it looks like there might be some syntactic sugar
user=> (type ss)
clojure.lang.PersistentHashSet
user=> ss
#{1 2 3 4}
user=> (def ss2 #{2 4 6 8})
#'user/ss2
user=> ss2
#{2 4 6 8}
user=> (type ss2)
clojure.lang.PersistentHashSet
user=> (type hm)
clojure.lang.PersistentHashMap
user=> hm
{:a 1, :b 2}
user=> (def hm2 {:b 2 :d 4 :f 6})
#'user/hm2
user=> (type hm2)
clojure.lang.PersistentHashMap
user=> hm2
{:b 2, :f 6, :d 4}
user=>
user=> ; how about iteration over all of these different sequence types?
user=> (doc for)
-------------------------
clojure.core/for
([seq-exprs body-expr])
Macro
List comprehension. Takes a vector of one or more
binding-form/collection-expr pairs, each followed by zero or more
modifiers, and yields a lazy sequence of evaluations of expr.
Collections are iterated in a nested fashion, rightmost fastest,
and nested coll-exprs can refer to bindings created in prior
binding-forms. Supported modifiers are: :let [binding-form expr ...],
:while test, :when test.
(take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))
nil
user=> (doc foreach)
nil
user=> (doc doseq)
-------------------------
clojure.core/doseq
([seq-exprs & body])
Macro
Repeatedly executes body (presumably for side-effects) with
bindings and filtering as provided by "for". Does not retain
the head of the sequence. Returns nil.
nil
user=> ; aha
user=> (doseq hm)
IllegalArgumentException doseq requires a vector for its binding clojure.core/doseq (core.clj:2804)
user=> (doseq [hm])
IllegalArgumentException doseq requires an even number of forms in binding vector clojure.core/doseq (core.clj:2805)
user=> (doseq [x hm])
nil
user=> (doseq [x hm] (println x))
[:a 1]
[:b 2]
nil
user=> (doseq [x ss] (println x))
1
2
3
4
nil
user=> (doseq [x '(1 2 3)] (println x))
1
2
3
nil
user=> (doseq [x [1 2 3]] (println x))
1
2
3
nil
user=> ; We wouldn't be in Lisp-land
user=> ; without functions being first class beasties
user=> ; so let's explore
user=> ;
user=> ;
user=> ; We already had the basics:
user=> (defun add2 [x] (+ x 2))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: defun in this context, compiling:(NO_SOURCE_PATH)
user=> (defn add2 [x] (+ x 2))
#'user/add2
user=> (type add2)
user$add2
user=> (add2 9)
11
user=> (add2 'a')
ClassCastException clojure.lang.Symbol cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:126)
user=> ; how about lambdas?
user=> (doc lambda)
nil
user=> (lambda x: (+ x 2))
RuntimeException Invalid token: x: clojure.lang.Util.runtimeException (Util.java:156)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH)
RuntimeException Unmatched delimiter: ) clojure.lang.Util.runtimeException (Util.java:156)
user=> (lambda [x] (+ x 2))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: lambda in this context, compiling:(NO_SOURCE_PATH)
user=> (lambda (x) (+ x 2))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: lambda in this context, compiling:(NO_SOURCE_PATH)
user=> (fn (x) (+ x 2))
CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(NO_SOURCE_PATH)
user=> (fn [x] (+ x 2))
#
user=> ; looks like we've stumbled on something
user=> (def add2_lambda (fn [x] (+ x 2)))
#'user/add2_lambda
user=> (type add2_lambda)
user$add2_lambda
user=> (add2_lambda 9)
11
user=> ; OK, how about making lambdas on the fly
user=> (defn make-adder [y] (fn [x] (+ x y)))
#'user/make-adder
user=> ((make-adder 5) 4)
9
user=> ((make-adder 15) 4)
19
user=> (doc defn)
-------------------------
clojure.core/defn
([name doc-string? attr-map? [params*] body] [name doc-string? attr-map? ([params*] body) + attr-map?])
Macro
Same as (def name (fn [params* ] exprs*)) or (def
name (fn ([params* ] exprs*)+)) with any doc-string or attrs added
to the var metadata
nil
user=> (doc fn)
-------------------------
clojure.core/fn
(fn name? [params*] exprs*)
(fn name? ([params*] exprs*) +)
Special Form
params => positional-params* , or positional-params* & next-param
positional-param => binding-form
next-param => binding-form
name => symbol
Defines a function
Please see http://clojure.org/special_forms#fn
nil
user=> ; so defn is a convenience macro that expands to doing (def name (fn ....
user=> ; which makes it pretty clear that Clojure is a Lisp-1
user=> ;
user=> ; there's also a hint of how to get varargs arguments
user=> ; (to mix metaphors and pull in a C-ism)
user=> (defn addup [x & r] (map (make-adder x) r))
#'user/addup
user=> (addup 10 1 2 3 4 5)
(11 12 13 14 15)
user=> ; how about &optional arguments?
user=> (defn with-opt [x y &optional z] (list x y z))
#'user/with-opt
user=> (doc with-opt)
-------------------------
user/with-opt
([x y &optional z])
nil
nil
user=> (with-opt 1 2)
ArityException Wrong number of args (2) passed to: user$with-opt clojure.lang.AFn.throwArity (AFn.java:437)
user=> (with-opt 1 2 3)
ArityException Wrong number of args (3) passed to: user$with-opt clojure.lang.AFn.throwArity (AFn.java:437)
user=> ; apparently not
user=> ; or at least, not like that
user=> ; try something like a destructuring-bind
user=> (defn with-opt [x y & [z]] (list x y z))
#'user/with-opt
user=> (with-opt 1 2 3)
(1 2 3)
user=> (with-opt 1 2)
(1 2 nil)
user=>
user=> ; OK, for an encore, how about keyword arguments
user=> (defn kw (&key x y z) (list x y z))
IllegalArgumentException Parameter declaration &key should be a vector clojure.core/assert-valid-fdecl (core.clj:6465)
user=> ; look this one up. An article converting Practial Common Lisp to Clojure is particularly useful
user=> (defn kw [{:keys x y z}] (list x y z))
CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(NO_SOURCE_PATH)
user=> (defn kw [{:keys [x y z]}] (list x y z))
#'user/kw
user=> (kw :x 1 :y 2)
ArityException Wrong number of args (4) passed to: user$kw clojure.lang.AFn.throwArity (AFn.java:437)
user=> (kw :x 1 :y 2 :z 3)
ArityException Wrong number of args (6) passed to: user$kw clojure.lang.AFn.throwArity (AFn.java:437)
user=> (kw {:x 1 :y 2})
(1 2 nil)
user=> (kw {:x 1 :y 2 :z 3})
(1 2 3)
user=> ; so it's more like a function that takes a single argument
user=> ; with a destructuring-bind to enforce a map structure on that single arg
user=> ; one more thing, spotted a while back
user=> (doc fn)
-------------------------
clojure.core/fn
(fn name? [params*] exprs*)
(fn name? ([params*] exprs*) +)
Special Form
params => positional-params* , or positional-params* & next-param
positional-param => binding-form
next-param => binding-form
name => symbol
Defines a function
Please see http://clojure.org/special_forms#fn
nil
user=> ; hmm, what's that plus doing at the end of the second form?
user=> (defn add-some [x y] (+ x y) [x y z] (+ x y z))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: z in this context, compiling:(NO_SOURCE_PATH)
user=> ; ah, forgot some Inconsequential Parentheses
user=> (defn add-some ([x y] (+ x y)) ([x y z] (+ x y z)))
#'user/add-some
user=> (add-some 1 2)
3
user=> (add-some 3 4 5)
12
user=> ; so we have overloading on number of arguments too
user=> ; What about clojures?
user=> ; I mean
user=> ; What about closures?
user=> (def add11 (make-adder 11))
#'user/add11
user=> (add11 9)
20
user=> ; as expected
user=> ; what about structures and classes and records and objects
user=> ; and so on
user=> ; we've seen a hash-map for less formally-structured collections of data
user=> ; is there anything more rigorously organized?
user=> (doc defclass)
nil
user=> (doc defstruct)
-------------------------
clojure.core/defstruct
([name & keys])
Macro
Same as (def name (create-struct keys...))
nil
user=> (doc create-struct)
-------------------------
clojure.core/create-struct
([& keys])
Returns a structure basis object.
nil
user=> (defstruct point :x :y)
#'user/point
user=> (doc point)
-------------------------
user/point
nil
nil
user=> (point :x 1 :y 2)
ClassCastException clojure.lang.PersistentStructMap$Def cannot be cast to clojure.lang.IFn user/eval7 (NO_SOURCE_FILE:6)
user=> (point [:x 1 :y 2])
ClassCastException clojure.lang.PersistentStructMap$Def cannot be cast to clojure.lang.IFn user/eval9 (NO_SOURCE_FILE:7)
user=> (point {:x 1 :y 2})
ClassCastException clojure.lang.PersistentStructMap$Def cannot be cast to clojure.lang.IFn user/eval11 (NO_SOURCE_FILE:8)
user=> (create-struct :x 1 :y 2)
#
user=> (point 1 2)
ClassCastException clojure.lang.PersistentStructMap$Def cannot be cast to clojure.lang.IFn user/eval15
(NO_SOURCE_FILE:10)
user=> (doc struct)
-------------------------
clojure.core/struct
([s & vals])
Returns a new structmap instance with the keys of the
structure-basis. vals must be supplied for basis keys in order -
where values are not supplied they will default to nil.
nil
user=> (struct point 1 2)
{:x 1, :y 2}
user=> (type point)
clojure.lang.PersistentStructMap$Def
user=> ; so a struct is just a map
user=> ; and we've just made a shortcut function for building a map
user=> ; with 2 known parameters in a known order (x then y)
user=> (def pp (struct point 1 2))
CompilerException java.lang.IllegalStateException: pp already refers to: #'clojure.pprint/pp in namespace: user, compiling:(NO_SOURCE_PATH)
user=> (def apoint (struct point 1 2))
#'user/apoint
user=> (apoint :x)
1
user=> (apoint :y)
2
user=> (type apoint)
clojure.lang.PersistentStructMap
user=> (dissoc apoint :x)
RuntimeException Can't remove struct key clojure.lang.Util.runtimeException (Util.java:156)
user=> (assoc apoint :z 3)
{:x 1, :y 2, :z 3}
user=> ; so there is the odd restriction
user=>
user=> (doc defrecord)
-------------------------
clojure.core/defrecord
([name [& fields] & opts+specs])
Macro
Alpha - subject to change
(defrecord name [fields*] options* specs*)
Currently there are no options.
Each spec consists of a protocol or interface name followed by zero
or more method bodies:
protocol-or-interface-or-Object
(methodName [args*] body)*
Dynamically generates compiled bytecode for class with the given
name, in a package with the same name as the current namespace, the
given fields, and, optionally, methods for protocols and/or
interfaces.
The class will have the (immutable) fields named by
fields, which can have type hints. Protocols/interfaces and methods
are optional. The only methods that can be supplied are those
declared in the protocols/interfaces. Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directy.
Method definitions take the form:
(methodname [args*] body)
The argument and return types can be hinted on the arg and
methodname symbols. If not supplied, they will be inferred, so type
hints should be reserved for disambiguation.
Methods should be supplied for all methods of the desired
protocol(s) and interface(s). You can also define overrides for
methods of Object. Note that a parameter must be supplied to
correspond to the target object ('this' in Java parlance). Thus
methods for interfaces will take one more argument than do the
interface declarations. Note also that recur calls to the method
head should *not* pass the target object, it will be supplied
automatically and can not be substituted.
In the method bodies, the (unqualified) name can be used to name the
class (for calls to new, instance? etc).
The class will have implementations of several (clojure.lang)
interfaces generated automatically: IObj (metadata support) and
IPersistentMap, and all of their superinterfaces.
In addition, defrecord will define type-and-value-based =,
and will defined Java .hashCode and .equals consistent with the
contract for java.util.Map.
When AOT compiling, generates compiled bytecode for a class with the
given name (a symbol), prepends the current ns as the package, and
writes the .class file to the *compile-path* directory.
Two constructors will be defined, one taking the designated fields
followed by a metadata map (nil for none) and an extension field
map (nil for none), and one taking only the fields (using nil for
meta and extension fields).
nil
user=> ; whoa
user=> ; there's a lot of stuff there
user=> ; particularly in comparison to the other docstrings
user=> ; doth protest too much, perhaps?
user=> ; can't help but notice the prominent "Alpha - subject to change"
user=> ; so maybe just move on
user=> ; OK, let's look at transfer of control
user=> ; the simplest way to get out of a function is to get to the end
user=> (defn f1 [x] (+ 1 x) 2)
#'user/f1
user=> (f1 10)
2
user=> ; looks like the last thing evaluated is returned
user=> ; in proper functional style
user=> ; any other expressions are being evaluated purely for their side effects
user=> ; (if you're grubby enough to do such a thing)
user=>
user=> (doc prog1)
nil
user=> (doc prog2)
nil
user=> (doc do)
-------------------------
do
(do exprs*)
Special Form
Evaluates the expressions in order and returns the value of
the last. If no expressions are supplied, returns nil.
Please see http://clojure.org/special_forms#do
nil
user=> (doc do1)
nil
user=> ; no obvious way to evaluate multiple things and return the first of them
user=> ; although it's easy to simulate with a temp
user=> (defn f2 [x]
(let [result (+ 1 x)]
2
result))
#'user/f2
user=> (f2 10)
11
user=> ; how about early bath conditions?
user=> (def to-ten '(1 2 3 4 5 6 7 8 9 10))
#'user/to-ten
user=> (% 10 2)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: % in this context, compiling:(NO_SOURCE_PATH)
user=> (mod 10 2)
0
user=> (doc mod)
-------------------------
clojure.core/mod
([num div])
Modulus of num and div. Truncates toward negative infinity.
nil
user=> (mod 3 3)
0
user=> (mod 4 3)
1
user=> (defn find-multiple [x s]
(doseq [val s]
(if (== (mod val x) 0) (return val))))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: return in this context, compiling:(NO_SOURCE_PATH)
user=> (doc return-from)
nil
user=> (doc return)
nil
user=> (doc ret)
nil
user=> (doc yield)
nil
user=> ; look it up on the Internet
user=> ; which basically means Stack Overflow these days, for programming questions
user=> ; so no dice
user=>
user=>
user=> ; how about exceptions
user=> (doc raise)
nil
user=> (doc throw)
-------------------------
throw
(throw expr)
Special Form
The expr is evaluated and thrown, therefore it should
yield an instance of some derivee of Throwable.
Please see http://clojure.org/special_forms#throw
nil
user=> (doc Throwable)
CompilerException java.lang.RuntimeException: Expecting var, but Throwable is mapped to class java.lang.Throwable, compiling:(NO_SOURCE_PATH)
user=> (Exception "fred")
RuntimeException Expecting var, but Exception is mapped to class java.lang.Exception clojure.lang.Util.runtimeException (Util.java:156)
user=> (Exception. "fred")
#
user=> ; (there's a cheat here. We'll come back to the magic dot)
user=> ; (or 'period', for the Americans amongst us)
user=> (throw (Exception. "fred"))
Exception fred user/eval59 (NO_SOURCE_FILE:94)
user=>
user=> (defn find-multiple [x s]
(doseq [val s]
(if (== (mod val x) 0) (throw (Exception. "not really that exceptional")))))
#'user/find-multiple
user=> (find-multiple 3 to-ten)
Exception not really that exceptional user/find-multiple (NO_SOURCE_FILE:131)
user=>
user=> ; like jugglers
user=> ; anything we throw
user=> ; we need to be able to catch
user=> (doc catch)
-------------------------
try
(try expr* catch-clause* finally-clause?)
Special Form
catch-clause => (catch classname name expr*)
finally-clause => (finally expr*)
Catches and handles Java exceptions.
Please see http://clojure.org/special_forms#try
nil
user=> (try (find-multiple 3 to-ten) (catch Exception xx (println "caught exception %s" xx)))
caught exception %s #
nil
user=> (try (find-multiple 3 to-ten) (catch Exception xx (println "caught exception %s" xx) (println "hit finally")))
caught exception %s #
hit finally
nil
user=> (try (find-multiple 13 to-ten) (catch Exception xx (println "caught exception %s" xx) (println "hit finally")))
nil
user=> ; ah
user=> ; need the magic "finally" incantation
user=> (try (find-multiple 13 to-ten) (catch Exception xx (println "caught exception %s" xx) (finally println "hit finally")))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: finally in this context, compiling:(NO_SOURCE_PATH)
user=> (try (find-multiple 13 to-ten) (catch Exception xx (println "caught exception %s" xx)) (println "hit finally"))
CompilerException java.lang.RuntimeException: Only catch or finally clause can follow catch in try expression, compiling:(NO_SOURCE_PATH)
user=> ; oops, got bracketing wrong
user=>
user=> (try (find-multiple 13 to-ten)
(catch Exception xx (println "caught exception %s" xx))
(finally println "hit finally"))
nil
user=> ;
user=> ; hmm, not hitting finally
user=> ; doh, need to make the print statement a proper expression
user=> (try (find-multiple 3 to-ten)
(catch Exception xx (println "caught exception %s" xx))
(finally (println "in finally clause")))
caught exception %s #
in finally clause
nil
user=> (try (find-multiple 13 to-ten)
(catch Exception xx (println "caught exception %s" xx))
(finally (println "in finally clause")))
in finally clause
nil
user=>
user=> (doc namespace)
-------------------------
clojure.core/namespace
([x])
Returns the namespace String of a symbol or keyword, or nil if not present.
nil
user=> (namespace x)
ClassCastException java.lang.Long cannot be cast to clojure.lang.Named clojure.core/namespace (core.clj:1496)
user=> (namespace 'x)
nil
user=> (namespace 'll)
nil
user=> (doc ns)
-------------------------
clojure.core/ns
([name docstring? attr-map? references*])
Macro
Sets *ns* to the namespace named by name (unevaluated), creating it
if needed. references can be zero or more of: (:refer-clojure ...)
(:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)
with the syntax of refer-clojure/require/use/import/load/gen-class
respectively, except the arguments are unevaluated and need not be
quoted. (:gen-class ...), when supplied, defaults to :name
corresponding to the ns name, :main true, :impl-ns same as ns, and
:init-impl-ns true. All options of gen-class are
supported. The :gen-class directive is ignored when not
compiling. If :gen-class is not supplied, when compiled only an
nsname__init.class will be generated. If :refer-clojure is not used, a
default (refer 'clojure) is used. Use of ns is preferred to
individual calls to in-ns/require/use/import:
(ns foo.bar
(:refer-clojure :exclude [ancestors printf])
(:require (clojure.contrib sql sql.tests))
(:use (my.lib this that))
(:import (java.util Date Timer Random)
(java.sql Connection Statement)))
nil
user=> (ns my-namespace)
nil
my-namespace=> *ns*
#
my-namespace=> (dir my-namespace)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: dir in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> (dir 'my-namespace)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: dir in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> (doc import)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: doc in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> ; let me out!
my-namespace=> (ns user)
nil
user=> (doc import)
-------------------------
clojure.core/import
([& import-symbols-or-lists])
Macro
import-list => (package-symbol class-name-symbols*)
For each name in class-name-symbols, adds a mapping from name to the
class named by package.name to the current namespace. Use :import in the ns
macro in preference to calling this directly.
nil
user=> (ns my-namespace :import clojure.core)
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword clojure.lang.RT.seqFrom (RT.java:487)
user=> (ns my-namespace :import (clojure.core))
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword clojure.lang.RT.seqFrom (RT.java:487)
user=> (ns my-namespace (:import (clojure.core)))
nil
my-namespace=> (doc dir)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: doc in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> (dir my-namespace)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: dir in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> (ns user)
nil
user=> (doc doc)
-------------------------
clojure.repl/doc
([name])
Macro
Prints documentation for a var or special form given its name
nil
user=> (ns my-namespace (:import (clojure.core) (clojure.repl)))
nil
my-namespace=> (doc doc)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: doc in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> ; let me out
my-namespace=> (ns user)
nil
user=> (ns my-namespace (:import (clojure.core import) (clojure.repl dir doc)))
ClassNotFoundException clojure.core.import java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (ns my-namespace (:import (clojure.core import) (clojure.repl/dir)))
ClassNotFoundException clojure.core.import java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (ns my-namespace)
nil
my-namespace=> (import (clojure.repl dir))
ClassNotFoundException clojure.repl.dir java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (ns user)
nil
user=> (doc import)
-------------------------
clojure.core/import
([& import-symbols-or-lists])
Macro
import-list => (package-symbol class-name-symbols*)
For each name in class-name-symbols, adds a mapping from name to the
class named by package.name to the current namespace. Use :import in the ns
macro in preference to calling this directly.
nil
user=> (doc require)
-------------------------
clojure.core/require
([& args])
Loads libs, skipping any that are already loaded. Each argument is
either a libspec that identifies a lib, a prefix list that identifies
multiple libs whose names share a common prefix, or a flag that modifies
how all the identified libs are loaded. Use :require in the ns macro
in preference to calling this directly.
Libs
A 'lib' is a named set of resources in classpath whose contents define a
library of Clojure code. Lib names are symbols and each lib is associated
with a Clojure namespace and a Java package that share its name. A lib's
name also locates its root directory within classpath using Java's
package name to classpath-relative path mapping. All resources in a lib
should be contained in the directory structure under its root directory.
All definitions a lib makes should be in its associated namespace.
'require loads a lib by loading its root resource. The root resource path
is derived from the lib name in the following manner:
Consider a lib named by the symbol 'x.y.z; it has the root directory
/x/y/, and its root resource is /x/y/z.clj. The root
resource should contain code to create the lib's namespace (usually by using
the ns macro) and load any additional lib resources.
Libspecs
A libspec is a lib name or a vector containing a lib name followed by
options expressed as sequential keywords and arguments.
Recognized options: :as
:as takes a symbol as its argument and makes that symbol an alias to the
lib's namespace in the current namespace.
Prefix Lists
It's common for Clojure code to depend on several libs whose names have
the same prefix. When specifying libs, prefix lists can be used to reduce
repetition. A prefix list contains the shared prefix followed by libspecs
with the shared prefix removed from the lib names. After removing the
prefix, the names that remain must not contain any periods.
Flags
A flag is a keyword.
Recognized flags: :reload, :reload-all, :verbose
:reload forces loading of all the identified libs even if they are
already loaded
:reload-all implies :reload and also forces loading of all libs that the
identified libs directly or indirectly load via require or use
:verbose triggers printing information about each load, alias, and refer
Example:
The following would load the libraries clojure.zip and clojure.set
abbreviated as 's'.
(require '(clojure zip [set :as s]))
nil
user=> (ns my-namespace (:import (clojure.core import) (clojure.repl dir)))
ClassNotFoundException clojure.core.import java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (ns my-namespace (:import (clojure.core) (clojure.repl dir)))
ClassNotFoundException clojure.repl.dir java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (ns my-namespace (:import (clojure.core) (clojure.repl)))
nil
my-namespace=> (ns user)
nil
user=> (ns my-namespace (:import (clojure.core) (clojure.repl)))
nil
my-namespace=> (doc dir)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: doc in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> (ns user)
nil
user=> (doc dir)
-------------------------
clojure.repl/dir
([nsname])
Macro
Prints a sorted directory of public vars in a namespace
nil
user=> (ns my-namespace (:import (clojure.core) (clojure.repl)))
nil
my-namespace=> (clojure.repl/dir)
ArityException Wrong number of args (0) passed to: repl$dir clojure.lang.Compiler.macroexpand1 (Compiler.java:6325)
my-namespace=> (clojure.repl/dir my-namespace)
nil
my-namespace=> (def x-in-mine 2)
#'my-namespace/x-in-mine
my-namespace=> (clojure.repl/dir my-namespace)
x-in-mine
nil
my-namespace=> ; OK, looks like a <package>/<symbol> is a fully qualified name
my-namespace=> ; just to confirm expected hiding
my-namespace=> (def x 10)
#'my-namespace/x
my-namespace=> x
10
my-namespace=> (ns user)
nil
user=> (def x 1)
#'user/x
user=> x
1
user=> (ns user)
nil
user=> (ns my-namespace)
nil
my-namespace=> x
10
my-namespace=> ; so how do I make a symbol from another package look like its local
my-namespace=> ; like a Python import?
my-namespace=> (ns temp-namespace)
nil
temp-namespace=> (clojure.repl/dir temp-namespace)
nil
temp-namespace=> ; ok, so I can get at things with an absolute name
temp-namespace=> ; even if there's been no sniff of an import
temp-namespace=> (ns my-namespace)
nil
my-namespace=> (clojure.repl/doc import)
-------------------------
clojure.core/import
([& import-symbols-or-lists])
Macro
import-list => (package-symbol class-name-symbols*)
For each name in class-name-symbols, adds a mapping from name to the
class named by package.name to the current namespace. Use :import in the ns
macro in preference to calling this directly.
nil
my-namespace=> (import clojure.repl doc)
ClassNotFoundException clojure.repl java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (import 'clojure.repl doc)
ClassNotFoundException clojure.repl java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (import 'clojure.repl/doc)
ClassNotFoundException doc java.net.URLClassLoader$1.run (URLClassLoader.java:202)
my-namespace=> (doc require)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: doc in this context, compiling:(NO_SOURCE_PATH)
my-namespace=> (clojure.repl/doc require)
-------------------------
clojure.core/require
([& args])
Loads libs, skipping any that are already loaded. Each argument is
either a libspec that identifies a lib, a prefix list that identifies
multiple libs whose names share a common prefix, or a flag that modifies
how all the identified libs are loaded. Use :require in the ns macro
in preference to calling this directly.
Libs
A 'lib' is a named set of resources in classpath whose contents define a
library of Clojure code. Lib names are symbols and each lib is associated
with a Clojure namespace and a Java package that share its name. A lib's
name also locates its root directory within classpath using Java's
package name to classpath-relative path mapping. All resources in a lib
should be contained in the directory structure under its root directory.
All definitions a lib makes should be in its associated namespace.
'require loads a lib by loading its root resource. The root resource path
is derived from the lib name in the following manner:
Consider a lib named by the symbol 'x.y.z; it has the root directory
/x/y/, and its root resource is /x/y/z.clj. The root
resource should contain code to create the lib's namespace (usually by using
the ns macro) and load any additional lib resources.
Libspecs
A libspec is a lib name or a vector containing a lib name followed by
options expressed as sequential keywords and arguments.
Recognized options: :as
:as takes a symbol as its argument and makes that symbol an alias to the
lib's namespace in the current namespace.
Prefix Lists
It's common for Clojure code to depend on several libs whose names have
the same prefix. When specifying libs, prefix lists can be used to reduce
repetition. A prefix list contains the shared prefix followed by libspecs
with the shared prefix removed from the lib names. After removing the
prefix, the names that remain must not contain any periods.
Flags
A flag is a keyword.
Recognized flags: :reload, :reload-all, :verbose
:reload forces loading of all the identified libs even if they are
already loaded
:reload-all implies :reload and also forces loading of all libs that the
identified libs directly or indirectly load via require or use
:verbose triggers printing information about each load, alias, and refer
Example:
The following would load the libraries clojure.zip and clojure.set
abbreviated as 's'.
(require '(clojure zip [set :as s]))
nil
my-namespace=> ; that sounds more about loading external libraries from
my-namespace=> ; disk rather than making existing things visible with a shorter name
my-namespace=> (clojure.repl/doc refer)
-------------------------
clojure.core/refer
([ns-sym & filters])
refers to all public vars of ns, subject to filters.
filters can include at most one each of:
:exclude list-of-symbols
:only list-of-symbols
:rename map-of-fromsymbol-tosymbol
For each public interned var in the namespace named by the symbol,
adds a mapping from the name of the var to the var to the current
namespace. Throws an exception if name is already mapped to
something else in the current namespace. Filters can be used to
select a subset, via inclusion or exclusion, or to provide a mapping
to a symbol different from the var's name, in order to prevent
clashes. Use :use in the ns macro in preference to calling this directly.
nil
my-namespace=> (clojure.core/refer clojure.repl)
CompilerException java.lang.RuntimeException: java.lang.ClassNotFoundException: clojure.repl, compiling:(NO_SOURCE_PATH)
my-namespace=> (clojure.core/refer 'clojure.repl)
nil
my-namespace=> (doc doc)
-------------------------
clojure.repl/doc
([name])
Macro
Prints documentation for a var or special form given its name
nil
my-namespace=> ; aha
my-namespace=> ; we've got it
my-namespace=>
Clojure 1.3.0
user=> ; let's play with introspection
user=> ; define a few things first
user=> ; so we've got a navel to gaze into
user=> ; so when we gaze into our navel
user=> ; we'll at least find some fluff
user=> (def x 1)
#'user/x
user=> (def y 2)
#'user/y
user=> (defn f [x] (+ x y))
#'user/f
user=> (f 5)
7
user=>
user=> (doc help
)
nil
user=> (doc doc)
-------------------------
clojure.repl/doc
([name])
Macro
Prints documentation for a var or special form given its name
nil
user=> (doc dir)
-------------------------
clojure.repl/dir
([nsname])
Macro
Prints a sorted directory of public vars in a namespace
nil
user=> *namespace*
CompilerException java.lang.RuntimeException: Unable to resolve symbol: *namespace* in this context, compiling:(NO_SOURCE_PATH)
user=> *ns*
#
user=> (dir *ns*)
Exception No namespace: *ns* found clojure.core/the-ns (core.clj:3689)
user=> (dir user)
f
x
y
nil
user=> (def ll (dir user))
f
ll
x
y
#'user/ll
user=> ll
nil
user=> ; so 'dir' prints out the symbols in the namespace rather than returning a sequence of them
user=> ; cheat and look up a couple of interesting things
user=> (ns-publics user)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: user in this context, compiling:(NO_SOURCE_PATH)
user=> (ns-publics 'user)
{ll #'user/ll, f #'user/f, x #'user/x, y #'user/y}
user=>
user=>
user=>
user=>
user=>
user=> (type (ns-publics 'user))
clojure.lang.PersistentArrayMap
user=> (def temp (ns-publics 'user))
#'user/temp
user=> (temp :ll)
nil
user=> (temp 'll)
#'user/ll
user=> (type (temp 'x))
clojure.lang.Var
user=> (temp 'f)
#'user/f
user=> (type (temp 'f))
clojure.lang.Var
user=>
user=> (ns-privates user)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: ns-privates in this context, compiling:(NO_SOURCE_PATH)
user=> (ns-interns user)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: user in this context, compiling:(NO_SOURCE_PATH)
user=> (ns-interns 'user)
{temp #'user/temp, ll #'user/ll, f #'user/f, x #'user/x, y #'user/y}
user=> (ns-map 'user)
{sorted-map #'clojure.core/sorted-map, read-line #'clojure.core/read-line, re-pattern #'clojure.core/re-pattern,
...snip...
Runtime java.lang.Runtime, keep #'clojure.core/keep, disj! #'clojure.core/disj!, meta #'clojure.core/meta}
user=> ; yikes
user=> ; that's including all of the imported/external stuff
user=>
user=> ; looking around on teh internet
user=> ; there's a useful Clojure script here
user=> ; with various tips and useful utils embedded therein
Clojure 1.3.0
user=> ;
user=> ; so the J in clojure comes from Java
user=> ; and we've seen a lot of mention of Java objects
user=> ; so let's see what we can get access to
user=> (type "fred")
java.lang.String
user=> (def x (java.lang.String 1))
ClassCastException java.lang.Class cannot be cast to clojure.lang.IFn clojure.lang.Compiler$InvokeExpr.eval (Compiler.java:3333)
user=> (doc java.lang.Integer)
CompilerException java.lang.RuntimeException: Unable to resolve var: java.lang.Integer in this context, compiling:(NO_SOURCE_PATH)
user=> (doc java.lang)
ClassNotFoundException java.lang java.net.URLClassLoader$1.run (URLClassLoader.java:202)
user=> ; first of all, how do I create a Java object?
user=> (doc Integer)
CompilerException java.lang.RuntimeException: Expecting var, but Integer is mapped to class java.lang.Integer, compiling:(NO_SOURCE_PATH)
user=> (doc java.lang/Integer)
nil
user=> (Integer 2)
RuntimeException Expecting var, but Integer is mapped to class java.lang.Integer clojure.lang.Util.runtimeException (Util.java:156)
user=>
user=> ; cheat and look up the magic dot macro
user=> (. Integer 2)
CompilerException java.lang.IllegalArgumentException: Malformed member expression, compiling:(NO_SOURCE_PATH)
user=> (new Integer)
CompilerException java.lang.IllegalArgumentException: No matching ctor found for class java.lang.Integer, compiling:(NO_SOURCE_PATH)
user=> (new Integer 2)
2
user=> ; aha
user=> (doc new)
-------------------------
new
(Classname. args*)
(new Classname args*)
Special Form
The args, if any, are evaluated from left to right, and
passed to the constructor of the class named by Classname. The
constructed object is returned.
Please see http://clojure.org/java_interop#new
nil
user=> (def i2 (new Integer 2))
#'user/i2
user=> i2
2
user=> ; presumably other constructors are similar
user=> (def i3 (new Integer "32"))
#'user/i3
user=> i3
32
user=> ; how do we invoke a method on an object?
user=> (i3 toHexString)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: toHexString in this context, compiling:(NO_SOURCE_PATH)
user=> (java.lang.Integer/toHexString i3)
"20"
user=> ; well that works, but it's a bit long-winded (** actually, not quite -- see below **)
user=> ; is there any syntactic sugar to make it a bit sweeter?
user=>
user=> ;
user=> (. i3 toHexString)
IllegalArgumentException No matching field found: toHexString for class java.lang.Integer clojure.lang.Reflector.getInstanceField (Reflector.java:289)
user=> (.toHexString i3)
IllegalArgumentException No matching field found: toHexString for class java.lang.Integer clojure.lang.Reflector.getInstanceField (Reflector.java:289)
user=> (type i3)
java.lang.Integer
user=> (. i3 toHexString)
IllegalArgumentException No matching field found: toHexString for class java.lang.Integer clojure.lang.Reflector.getInstanceField (Reflector.java:289)
user=> (doc .)
-------------------------
.
(.instanceMember instance args*)
(.instanceMember Classname args*)
(Classname/staticMethod args*)
Classname/staticField
Special Form
The instance member form works for both fields and methods.
They all expand into calls to the dot operator at macroexpansion time.
Please see http://clojure.org/java_interop#dot
nil
user=> (.toHexString i3)
IllegalArgumentException No matching field found: toHexString for class java.lang.Integer clojure.lang.Reflector.getInstanceField (Reflector.java:289)
user=> (. toHexString i3)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: toHexString in this context, compiling:(NO_SOURCE_PATH)
user=> (.intValue i3)
32
user=> ; doh
user=> ; toHexString is a static method
user=> ; so just (.<methodname> <self> <args*>) is method invocation
user=> ; speaking of which, we've seen how to get at static methods
user=> (java.lang.Integer/toHexString 100)
"64"
user=> ; turns out there's another way too
user=> (. java.lang.Integer toHexString 100)
"64"
user=>
user=> ; how about a more realistic example
user=> (def abpattern (. java.util.regexp.Pattern compile "a*b"))
CompilerException java.lang.RuntimeException: java.lang.ClassNotFoundException: java.util.regexp.Pattern, compiling:(NO_SOURCE_PATH)
user=> (def abpattern (. java.util.regex.Pattern compile "a*b"))
#'user/abpattern
user=> (type abpattern)
java.util.regex.Pattern
user=> abpattern
#"a*b"
user=> ; ooh, interesting
user=> ; looks like there's some syntactic sugar
user=> #"a*b*c"
#"a*b*c"
user=> (type #"a*b*c")
java.util.regex.Pattern
user=> ; useful
user=> (def m (.matcher abpattern "aaaaab"))
#'user/m
user=> (type m)
java.util.regex.Matcher
user=> m
#
user=> (.matches m)
true
user=> (def m (.matcher abpattern "ccccb"))
#'user/m
user=> (.matches m)
false
Copyright (c) 2012, David Drysdale
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is available here.