Clojure Essentials

Basic Syntax

 1;; Comments start with semicolon
 2
 3;; Variables (immutable by default)
 4(def x 42)
 5(def pi 3.14159)
 6
 7;; Functions
 8(defn square [x]
 9  (* x x))
10
11(defn greet [name]
12  (str "Hello, " name "!"))
13
14;; Multi-arity functions
15(defn greet
16  ([] (greet "World"))
17  ([name] (str "Hello, " name "!")))
18
19;; Anonymous functions
20(fn [x] (* x x))
21#(* % %)  ; Shorthand
22((fn [x] (* x x)) 5)  ; => 25
23
24;; Let bindings
25(let [x 10
26      y 20]
27  (+ x y))  ; => 30

Data Structures (Immutable)

Lists

 1;; Create lists
 2'(1 2 3 4)
 3(list 1 2 3 4)
 4
 5;; Access
 6(first '(1 2 3))   ; => 1
 7(rest '(1 2 3))    ; => (2 3)
 8(nth '(1 2 3) 1)   ; => 2
 9
10;; Operations
11(cons 0 '(1 2 3))  ; => (0 1 2 3)
12(conj '(1 2 3) 0)  ; => (0 1 2 3) (adds to front)

Vectors

 1;; Create vectors
 2[1 2 3 4]
 3(vector 1 2 3 4)
 4
 5;; Access
 6(get [1 2 3] 1)    ; => 2
 7([1 2 3] 1)        ; => 2 (vector as function)
 8(nth [1 2 3] 1)    ; => 2
 9
10;; Operations
11(conj [1 2 3] 4)   ; => [1 2 3 4] (adds to end)
12(assoc [1 2 3] 1 99)  ; => [1 99 3]
13(subvec [1 2 3 4 5] 1 4)  ; => [2 3 4]

Maps

 1;; Create maps
 2{:name "John" :age 30}
 3(hash-map :name "John" :age 30)
 4
 5;; Access
 6(get {:name "John"} :name)  ; => "John"
 7(:name {:name "John"})      ; => "John" (keyword as function)
 8({:name "John"} :name)      ; => "John" (map as function)
 9
10;; Operations
11(assoc {:name "John"} :age 30)  ; => {:name "John" :age 30}
12(dissoc {:name "John" :age 30} :age)  ; => {:name "John"}
13(merge {:a 1} {:b 2})  ; => {:a 1 :b 2}
14(update {:count 5} :count inc)  ; => {:count 6}

Sets

 1;; Create sets
 2#{1 2 3 4}
 3(hash-set 1 2 3 4)
 4
 5;; Operations
 6(conj #{1 2 3} 4)  ; => #{1 2 3 4}
 7(disj #{1 2 3 4} 2)  ; => #{1 3 4}
 8(contains? #{1 2 3} 2)  ; => true
 9
10;; Set operations
11(clojure.set/union #{1 2} #{2 3})  ; => #{1 2 3}
12(clojure.set/intersection #{1 2 3} #{2 3 4})  ; => #{2 3}
13(clojure.set/difference #{1 2 3} #{2 3 4})  ; => #{1}

Control Flow

Conditionals

 1;; if
 2(if (> x 10)
 3  "Greater"
 4  "Not greater")
 5
 6;; when (no else clause)
 7(when (> x 10)
 8  (println "Greater")
 9  (do-something))
10
11;; cond
12(cond
13  (< x 0) "Negative"
14  (= x 0) "Zero"
15  (> x 0) "Positive"
16  :else "Unknown")
17
18;; case
19(case day
20  :monday "Start of week"
21  :friday "End of week"
22  (:saturday :sunday) "Weekend"
23  "Midweek")
24
25;; if-let (bind and test)
26(if-let [result (find-something)]
27  (println "Found:" result)
28  (println "Not found"))
29
30;; when-let
31(when-let [result (find-something)]
32  (println "Found:" result)
33  (process result))

Sequences and Collections

Sequence Operations

 1;; map
 2(map inc [1 2 3 4])  ; => (2 3 4 5)
 3(map + [1 2 3] [10 20 30])  ; => (11 22 33)
 4
 5;; filter
 6(filter even? [1 2 3 4 5 6])  ; => (2 4 6)
 7(remove odd? [1 2 3 4 5 6])   ; => (2 4 6)
 8
 9;; reduce
10(reduce + [1 2 3 4 5])  ; => 15
11(reduce + 100 [1 2 3])  ; => 106 (with initial value)
12
13;; take/drop
14(take 3 [1 2 3 4 5])  ; => (1 2 3)
15(drop 2 [1 2 3 4 5])  ; => (3 4 5)
16
17;; partition
18(partition 2 [1 2 3 4 5 6])  ; => ((1 2) (3 4) (5 6))
19(partition-all 2 [1 2 3 4 5])  ; => ((1 2) (3 4) (5))
20
21;; group-by
22(group-by even? [1 2 3 4 5 6])  ; => {false [1 3 5], true [2 4 6]}
23
24;; sort
25(sort [3 1 4 1 5 9])  ; => (1 1 3 4 5 9)
26(sort-by :age [{:name "John" :age 30} {:name "Jane" :age 25}])

Lazy Sequences

 1;; range
 2(range 10)  ; => (0 1 2 3 4 5 6 7 8 9)
 3(range 5 10)  ; => (5 6 7 8 9)
 4
 5;; repeat
 6(take 5 (repeat "x"))  ; => ("x" "x" "x" "x" "x")
 7
 8;; cycle
 9(take 5 (cycle [1 2 3]))  ; => (1 2 3 1 2)
10
11;; iterate
12(take 5 (iterate inc 0))  ; => (0 1 2 3 4)
13
14;; lazy-seq (custom lazy sequence)
15(defn fibonacci
16  ([] (fibonacci 0 1))
17  ([a b] (lazy-seq (cons a (fibonacci b (+ a b))))))
18
19(take 10 (fibonacci))  ; => (0 1 1 2 3 5 8 13 21 34)

Destructuring

 1;; Vector destructuring
 2(let [[a b c] [1 2 3]]
 3  (+ a b c))  ; => 6
 4
 5;; With rest
 6(let [[first & rest] [1 2 3 4 5]]
 7  [first rest])  ; => [1 (2 3 4 5)]
 8
 9;; Map destructuring
10(let [{:keys [name age]} {:name "John" :age 30}]
11  (str name " is " age))  ; => "John is 30"
12
13;; With defaults
14(let [{:keys [name age] :or {age 0}} {:name "John"}]
15  age)  ; => 0
16
17;; Function parameters
18(defn greet [{:keys [name age]}]
19  (str name " is " age " years old"))
20
21(greet {:name "John" :age 30})

Macros

 1;; Simple macro
 2(defmacro unless [test & body]
 3  `(if (not ~test)
 4     (do ~@body)))
 5
 6(unless false
 7  (println "This will print"))
 8
 9;; Macro with gensym
10(defmacro with-logging [expr]
11  `(let [result# ~expr]
12     (println "Result:" result#)
13     result#))
14
15;; Threading macros
16(-> 5
17    (+ 3)
18    (* 2)
19    (- 1))  ; => 15 (same as (- (* (+ 5 3) 2) 1))
20
21(->> [1 2 3 4 5]
22     (map inc)
23     (filter even?)
24     (reduce +))  ; => 12

Namespaces

 1;; Define namespace
 2(ns myapp.core
 3  (:require [clojure.string :as str]
 4            [clojure.set :as set]))
 5
 6;; Use functions
 7(str/upper-case "hello")  ; => "HELLO"
 8(set/union #{1 2} #{2 3})  ; => #{1 2 3}
 9
10;; Refer specific functions
11(ns myapp.core
12  (:require [clojure.string :refer [upper-case lower-case]]))
13
14(upper-case "hello")  ; => "HELLO"

State Management

Atoms (Synchronous, Independent)

 1;; Create atom
 2(def counter (atom 0))
 3
 4;; Read
 5@counter  ; => 0
 6
 7;; Update
 8(swap! counter inc)  ; => 1
 9(swap! counter + 10)  ; => 11
10
11;; Set
12(reset! counter 0)  ; => 0

Refs (Synchronous, Coordinated)

 1;; Create refs
 2(def account1 (ref 1000))
 3(def account2 (ref 500))
 4
 5;; Transaction
 6(dosync
 7  (alter account1 - 100)
 8  (alter account2 + 100))
 9
10@account1  ; => 900
11@account2  ; => 600

Agents (Asynchronous)

 1;; Create agent
 2(def logger (agent []))
 3
 4;; Send action
 5(send logger conj "Log entry 1")
 6(send logger conj "Log entry 2")
 7
 8;; Wait for completion
 9(await logger)
10
11@logger  ; => ["Log entry 1" "Log entry 2"]

Java Interop

 1;; Call static method
 2(Math/pow 2 3)  ; => 8.0
 3
 4;; Create object
 5(def date (java.util.Date.))
 6
 7;; Call method
 8(.getTime date)
 9
10;; Access field
11(.-field object)
12
13;; Import
14(import '(java.util Date Calendar))
15(import '[java.io File FileReader])
16
17;; Chaining
18(.. "hello"
19    (toUpperCase)
20    (substring 0 3))  ; => "HEL"

Practical Examples

File I/O

1;; Read file
2(slurp "file.txt")
3
4;; Write file
5(spit "output.txt" "Hello, World!")
6
7;; Read lines
8(with-open [rdr (clojure.java.io/reader "file.txt")]
9  (doall (line-seq rdr)))

HTTP Request

 1;; Using clj-http library
 2(require '[clj-http.client :as http])
 3
 4;; GET request
 5(http/get "https://api.example.com/data")
 6
 7;; POST request
 8(http/post "https://api.example.com/data"
 9           {:body (json/write-str {:key "value"})
10            :headers {"Content-Type" "application/json"}})

JSON Processing

 1;; Using cheshire library
 2(require '[cheshire.core :as json])
 3
 4;; Parse JSON
 5(json/parse-string "{\"name\":\"John\",\"age\":30}" true)
 6; => {:name "John" :age 30}
 7
 8;; Generate JSON
 9(json/generate-string {:name "John" :age 30})
10; => "{\"name\":\"John\",\"age\":30}"

Testing

 1(ns myapp.core-test
 2  (:require [clojure.test :refer :all]
 3            [myapp.core :refer :all]))
 4
 5(deftest test-square
 6  (testing "Square function"
 7    (is (= 4 (square 2)))
 8    (is (= 9 (square 3)))))
 9
10(deftest test-greet
11  (testing "Greet function"
12    (is (= "Hello, John!" (greet "John")))))

Further Reading

Related Snippets