Clojure
20.1 Philosophy
Clojure (Rich Hickey, 2007) is a modern Lisp hosted on the JVM (and on JavaScript as ClojureScript, on .NET as ClojureCLR, and natively via Babashka/GraalVM). Its emphasis is immutable persistent data structures, functional programming, concurrency, and pragmatic host interop.
20.2 Data and Sequences
;; Literals: list, vector, map, set, keyword, regex, char
'(1 2 3) [1 2 3] {:a 1 :b 2} #{1 2 3} :keyword #"\d+" \c
(conj [1 2] 3) ; [1 2 3] (vectors add at the end)
(conj '(1 2) 0) ; (0 1 2) (lists add at the front)
(get-in {:user {:name "Ana"}} [:user :name]) ; "Ana"
(assoc-in {:user {:name "Ana"}} [:user :age] 30)
(update-in {:counts {:a 1}} [:counts :a] inc)
;; The sequence abstraction: works on every collection
(map inc [1 2 3]) ; (2 3 4)
(filter even? #{1 2 3 4})
(take 5 (iterate #(* 2 %) 1)) ; (1 2 4 8 16): lazy infinite sequence
(group-by count ["a" "bb" "cc"]) ; {1 ["a"], 2 ["bb" "cc"]}
(frequencies "mississippi") ; {\m 1, \i 4, \s 4, \p 2}
(into {} (map (fn [x] [x (* x x)]) (range 3))) ; {0 0, 1 1, 2 4}
;; Destructuring
(let [[a b & more] [1 2 3 4]
{:keys [name age] :or {age 0}} {:name "Ana"}]
[a b more name age]) ; [1 2 (3 4) "Ana" 0]
(defn greet [{:keys [name greeting] :or {greeting "Hello"}}]
(str greeting ", " name "!"))
(greet {:name "Bo"}) ; "Hello, Bo!"20.3 Polymorphism: Protocols, Records, Multimethods
(defprotocol Shape
(area [this])
(perimeter [this]))
(defrecord Circle [r]
Shape
(area [_] (* Math/PI r r))
(perimeter [_] (* 2 Math/PI r)))
(defrecord Rect [w h]
Shape
(area [_] (* w h))
(perimeter [_] (* 2 (+ w h))))
(map area [(->Circle 1) (->Rect 2 3)]) ; (3.14159... 6)
(defmulti speak :type) ; dispatch on any function of the arguments
(defmethod speak :dog [_] "Woof")
(defmethod speak :cat [_] "Meow")
(defmethod speak :default [_] "...")
(speak {:type :dog}) ; "Woof"20.4 State and Concurrency
(def counter (atom 0)) ; atom: uncoordinated, synchronous
(swap! counter inc) ; 1
(reset! counter 10)
@counter ; 10 (deref)
(def account-a (ref 100)) ; refs: coordinated via STM
(def account-b (ref 0))
(dosync
(alter account-a - 30)
(alter account-b + 30))
(def logger (agent [])) ; agents: asynchronous
(send logger conj "event")
(def result (future (Thread/sleep 100) 42)) ; runs on another thread
@result ; 42
(require '[clojure.core.async :refer [chan go >! <! <!! timeout]])
(let [c (chan)]
(go (>! c "hello from a go block"))
(<!! c)) ; CSP-style channels (core.async)20.5 Interop, Tooling and Ecosystem
Tools: deps.edn + Clojure CLI, Leiningen, Babashka (fast-starting scripts), clj-kondo (linter), Calva/CIDER/Cursive, nREPL.
Ecosystem: Ring/Compojure/Reitit (web), Pedestal, Kit, next.jdbc (SQL), HoneySQL, Datomic/XTDB/Datalevin (databases), clojure.spec/Malli (data specs and validation), re-frame/Reagent (ClojureScript UIs), shadow-cljs.
(.toUpperCase "hello") ; Java method call
(Math/sqrt 16) ; static method
(java.util.UUID/randomUUID)
(doto (java.util.ArrayList.) (.add 1) (.add 2))