In a terminal type:
lein repl
; semi-colon marks the rest of the line as a comment
; This is the read prompt, type stuff at it and it will be evaluated
user=>
user=> (println "Hello from REPL")
Hello from REPL
nil
- Long, BigInt
- Rational
- Double, BigDecimal
; Prefer Long to integer
user=> (type 123456)
java.lang.Long
; And Ratio to decimals
user=> (/ 22 7)
22/7
user=> (type 22/7)
clojure.lang.Ratio
- "Strings"
- \c \h \a \r \s
- Symbols
- :Keywords
user=> (fn [arg] arg)
#<user$eval678$fn__679 user$eval678$fn__679@1afcfd10> ; uh-oh wat?
; Wrap the function in parens to evaluate it.
user=> ((fn [arg] arg) 10)
10
; Vectors
user=> [1 2 3 4 5 6]
[1 2 3 4 5 6]
; Maps
user=> {:one 1 :two 2 :three 3}
{:one 1, :three 3, :two 2}
; Sets
user=> #{1 2 3 4 5 6}
#{1 2 3 4 5 6}
; Lists
user=> (1 2 3 4 5 6)
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval702 (NO_SOURCE_FILE:1)
; Oops. That didn't work
Operator first, operands later
Instead of 1 + 2
use + 1 2
Instead of 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8
use + 1 2 3 4 5 6 7 8
In clojure the first item inside parens will be evaluated with the rest of the items as arguments.
user=> (+ 1 2)
3
; many arity example
;(operator operand1 operand2 .. operandN)
user=> (+ 1 2 3 4 5 6 7 8)
36
s-exps treeify your code
user=> (* 2 (+ 3 4))
14
Remember this
user=> (1 2 3 4 5 6)
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval702 (NO_SOURCE_FILE:1)
To prevent evaluate the list can be quoted
user=> (quote (1 2 3 4 5 6))
(1 2 3 4 5 6)
user=> `(1 2 3 4 5 6) ; quote shortcut
(1 2 3 4 5 6)
Namespaces allow you to bundle related things together
; The current namespace
user=> *ns*
#<Namespace user>
; Changing namespace
user=> (ns some.new.namespace)
nil
some.new.namespace=>
Use def
to create things inside the namespace
some.new.namespace=> (def one 1)
#'some.new.namespace/one
some.new.namespace=> one
1
; In other namespaces the symbol has to be qualified
some.new.namespace=> (ns user)
nil
user=> one ; won't work
CompilerException java.lang.RuntimeException: Unable to resolve symbol: one in this context, compiling:(NO_SOURCE_PATH:0)
user=> some.new.namespace/one
1
; The value can be used in normal operations
user=> (+ 1 some.new.namespace/one)
2
Remember symbols from data types above? Symbols are used to name things and evaluate to the thing they name, in our code one
is a symbol and it evaluates to 1.
user=> (def a-func (fn [arg] arg))
#'user/a-func
user=> a-func
#<user$a_func user$a_func@78c5cc63>
user=> (a-func 10)
10
user=> (a-func "ten")
"ten"
defn
is a shortcut for (def ... (fn ...))
user=> (defn b-func [arg] arg)
#'user/b-func
user=> (b-func 10)
10
Scoping is lexical.
Using let
user=> (let [one 1
#_=> two 2]
#_=> (+ one two))
3
user=> (+ one two) ; Outside scope where one and two don't exist
CompilerException java.lang.RuntimeException: Unable to resolve symbol: one in this context, compiling:(NO_SOURCE_PATH:1)
Map over a collection
user=> (map inc [1 2 3 4 5])
(2 3 4 5 6)
Filter a collection
user=> (filter even? [1 2 3 4 5])
(2 4)
Invert filter with remove
user=> (remove even? [1 2 3 4 5])
(1 3 5)
Reduce a collection
user=> (reduce + [1 2 3 4 5])
15
Map is many arity
user=> (map + [1 2 3 4 5] [6 7 8 9 10] [10 20 30 40 50] [60 70 80 90 100])
(77 99 121 143 165)
; Additional values are ignored
user=> (map + [1 2 3 4 5] [6 7 8 9 10 11 12 13 14 15 16 17 18])
(7 9 11 13 15)
Parallelized mapping with pmap
. (use this when the function is intensive or slow (i.e not for addition))
user=> (pmap + [1 2 3 4 5] [6 7 8 9 10] [10 20 30 40 50] [60 70 80 90 100])
(77 99 121 143 165)
Evaluate a many arity function with all list contents its arguments
user=> (apply + [1 2 3 4 5])
15
Side note: Many Clojure functions are higher order. Here's a shortcut to creating throwaway functions to pass as arguments
; Replace this function for squaring the arg
user=> (fn [arg] (* arg arg))
#<user$eval825$fn__826 user$eval825$fn__826@376433e4>
; With this
user=> #(* % %) ; #( represent the reader macro, % represents the first argument
#<user$eval947$fn__948 user$eval947$fn__948@40e4d37f>
user=> #(* %1 %1) ; The equivalent with numbered argument
#<user$eval837$fn__838 user$eval837$fn__838@635b9f9a>
user=> #(* %1 %2) ; Here this is a function of arity two that multiplies the operands
#<user$eval845$fn__846 user$eval845$fn__846@37b24706>
Clojure supports partial functions using partial
user=> (map (partial + 3) [1 2 3 4 5])
(4 5 6 7 8)
And function composition using comp
user=> (map (comp inc inc inc) [1 2 3 4 5])
(4 5 6 7 8)
Sequential collections can be accessed by index with nth
user=> (nth [1 2 3 4 5] 1)
2
; Vectors are also functions that take an index as an argument
user=> ([1 2 3 4 5] 2)
3
The head and tail of a list can be taken with first
and rest
.
user=> (first [1 2 3 4 5])
1
user=> (rest [1 2 3 4 5])
(2 3 4 5)
user=> (take 2 [1 2 3 4 5])
(1 2)
user=> (drop 2 [1 2 3 4 5])
(3 4 5)
range
builds ranges
user=> (range 10)
(0 1 2 3 4 5 6 7 8 9)
user=> (range 5 10)
(5 6 7 8 9)
user=> (range) ; Without an upper bound this creates an infinite sequance. Don't run this in the repl!
take
is handy for sampling infinite sequences in the repl
user=> (take 100 (range))
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99)
Some other infinite sequences
; repeat accepts a value creates an infinite seq full of the same value
user=> (take 10 (repeat "Clojure"))
("Clojure" "Clojure" "Clojure" "Clojure" "Clojure" "Clojure" "Clojure" "Clojure" "Clojure" "Clojure")
; cycle accepts a collection and creats and infinite seq looping the collection
user=> (take 10 (cycle [1 2 3]))
(1 2 3 1 2 3 1 2 3 1)
interleave
merges sequences together
user=> (take 10 (interleave (repeat "Rich") (repeat "Hickey")))
("Rich" "Hickey" "Rich" "Hickey" "Rich" "Hickey" "Rich" "Hickey" "Rich" "Hickey")
group-by
accepts a function and groups the items in the sequence by the result of function evaluation
user=> (group-by (partial > 5) [1 2 3 4 5 6 7 8 9 10])
{true [1 2 3 4], false [5 6 7 8 9 10]}
Similarly(ish), frequencies
can tell you how many times an item appears in a sequence
user=> (frequencies [1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 5 5 5 5 6 6 6 7 7 8])
{1 8, 2 7, 3 6, 4 5, 5 4, 6 3, 7 2, 8 1}
Maps are constructed in Clojure using curly braces. Commas are insignificant and treated as whitespace but it's idiomatic Clojure to separate each key-value pair by a comma.
user=> (def some-map {:one 1, :two 2, :three 3})
#'user/some-map
user=> some-map
{:one 1, :three 3, :two 2}
The colon prefixed symbols are keywords, and they are often used as map keys.
Maps are also functions that take a single key argument and return the value associated with that key
user=> (some-map :one)
1
Conversely, a keyword is also a function, one that will take a single map argument and lookup itself in that map
user=> (:one some-map)
1
Maps are often used as containers for named data fields
user=> (def important-data {:a-number 123, :some-name "Bob", :user-monster-adoption-monster-id 1234565432256765})
#'user/important-data
In Clojure everything is immutable. Apart from when it isn't. And when it isn't, Rich Hickey is there to save us.
Clojure has four mutable types
- Refs - use when multiple values need to be updated in the same transaction
- Atoms - Synchronous and will be retried until complete
- Agents - Asynchronous and will only be run once. Often used for IO.
- Vars - Thread safe, used for dynamic binding. More later.
user=> (def refxample (ref {})) ; Create a ref containing a map
#'user/refxample
user=> refxample
#<Ref@33e1ccbc: {}>
user=> (deref refxample) ; deref the ref to get the map
{}
user=> @refxample ; @ is a shortcut to deref
{}
Refs can be updated using alter inside a transaction
user=> (alter refxample assoc :key "value") ; this isn't in a transaction
IllegalStateException No transaction running clojure.lang.LockingTransaction.getEx (LockingTransaction.java:208)
user=> (dosync (alter refxample assoc :key "value")) ; dosync evaluates its body inside a transaction
{:key "value"}
user=> @refxample
{:key "value"}
user=> (def atomexample (atom 10)) ; Create an atom with value 10
#'user/atomexample
user=> @atomexample
10
user=> (swap! atomexample inc) ; Update the atom by calling inc on the current value
11
user=> @atomexample
11
Vars are interesting because they are dynamically bound and thread safe. Enclosing the var name in asterisks (often called earmuffs) is idiomatic
user=> (def ^:dynamic *some-var* 10)
#'user/*some-var*
user=> *some-var*
10
user=> (binding [*some-var* 20]
#_=> (println *some-var*))
20
nil
Because the binding is dynamic instead of lexical, it passes through function barriers
user=> (def ^:dynamic *some-var* 10)
#'user/*some-var*
#'user/print-some-var
user=> (defn print-some-var []
#_=> (println *some-var*)
#_=> *some-var*)
#'user/print-some-var
user=> (binding [*some-var* 20]
#_=> (print-some-var))
20
20
user=> (print-some-var)
10
10
This has only been a brief run through, there's more out there!
Display documenation
(doc function-name)
Display source code
(source function-name)
Search for documentation
(find-doc "what are you looking for?")
Show javadoc
(javadoc class-name)