6. A form is a list where the first symbol in the
list has to be a special word that the
compiler can understand - usually the name
of a function
7. Clojure Features
● Functional
○ Immutable, persistent data structures
○ Functions are objects
● Lisp
● Hosted on JVM
○ the same memory model
○ garbage collector
○ etc
● Supporting Concurrency
● Open source
8. Clojure Features
● Dynamic development
○ REPL (read-eval-print-loop), on-the-fly
compilation to JVM bytecode
● Full set of immutable, extensible, read-only
data structures:
○ lists
○ maps
○ sets
○ vectors
● Macros
12. Concurrent programming problems
Basic solution is
● locks
● synchronized access
● only one thread can have
lock, others blocks
● But it requires additional efforts
○ thread-coordination
○ avoiding deadlocks
13. Concurrent programming problems
Access problem
● several threads are trying to
access and change the same
shared data at the same time
● write/read at the same time
● readers block readers
14. Clojure way
● There is no any possibility to
change data by direct access. All
data structures are immutable,
remember?
● Only read, if we add element -
new data structure will be
created.
● No locks
● No access problems
15. But what if
we really want change
data in Clojure?
This opportunity is also
provided by the language! All
hard work done for us.
17. Vars
● Reference to mutable storage location
● Dynamically rebound on a per-thread basis
● Functions are vars, so they can be dynamically rebound too
● Ensure safe use via thread-isolation
(def x 5) // root-binding
(x) // 5
....
(binding [x 20]
(+ x 1)) // 21
....
(x) // 5 again, not changed
18. Changing Vars
(set! var-name new-value)
● cannot change root-binding
● works only in thread-local context (in "binding")
(def x 5) // root-binding
(set! x 7) // exception will be thrown
....
(binding [x 20]
(println (+ x 1)) // 21
(set! x 10)
(+ x 5)) // 15
....
(x) // 5 again, not changed
19. Refs
● Based on Software Transactional Memory (STM)
● Change the value by applying a function to old value
● Refs can be changed within a transaction only
● Transactions will be retried automatically if conflict happens
● To make changes code must be surrounded with (dosync
...)
● All changes are atomic
21. Refs lifecycle
(def r (ref '(1 2 3)))
(deref r)
(dosync
....
if any other transaction
(alter r
commutes - this
(fn [_] '(10 9 8))
transaction is repeated
...
)
Otherwise, that's ok and
(deref r) programs continues
execution
22. Atoms
● Way to manage shared state synchronously
● Change the value by applying a function to old value
● This is done in an atomic manner by function swap!
● Internally, swap! reads the current value, appliesthe
function to it, and attemprs to call compare-and-set! for this
atom
23. Atoms best practices by Rich Hickey
(defn memoize [f]
(let [mem (atom {})]
(fn [& args]
(if-let [e (find @mem args)]
(val e)
(let [ret (apply f args)]
(swap! mem assoc args ret)
ret)))))
24. Atoms best practices by Rich Hickey
(defn fib [n]
(if (<= n 1)
n
(+ (fib (dec n)) (fib (- n 2)))))
(time (fib 35))
user=> "Elapsed time: 941.445 msecs"
(def fib (memoize fib))
(time (fib 35))
user=> "Elapsed time: 0.044 msecs"
25. Agents
● Asynchronous changing. They are not blocking main
execution, return immediately
● Change the value by applying a function to old value
● Agent action dispatches take the
form (send agent fn args*)
● If other actions try to change data - they will be added to
the queue
26. Agents
(def a (agent 5)) // create agent
(deref a) // 5
(send a
(fn [old-val]
(+ old-val 5)))
(deref a) // may be 5, or may be 10
(Thread/sleep 1000)
(deref a) // 10