Living Clojure

(reading notes)

Preparation

  1. install java
  2. install Leiningen
  3. create a new project lein new wonderland
  4. entering REPL: cd wonderland; lein repl

Chapter 1. The Structure of Clojure

simple values or literals simply evaluate to themselves, for example:

  • integer: 12
  • decimal: 12.43
  • ratio: 1/3
  • character: \j
  • keyword: :jam
  • string: "jam"
  • boolean: true, false
  • null: nil

expressions

(+ 1 (+ 8 3))

collections

  • lists
  • vectors
  • maps
  • sets

all collections are immutable and persistent.

Immutable means the value of the collection does not change. A function to change a collection gives you back a new version of the collection.

Persistent means collections will do smart creations of new versions of themselves by using structural sharing.

Lists

Lists are collections of data what you want to access from the top of the list.

to create a list, simply put a quote in front of the parents:

'(1 2 "jam" :marmalade-jar)

you can mix and match values in a collections.

this is also valid:

'(1, 2, "jam", :bee)

commas are ignored and treated like whitespace. (but just use spaces)

some basic functions:

(first '(:rabbit :pocket-watch :marmalade :door))
;; -> :rabbit
(rest '(:rabbit :pocket-watch :marmalade :door))
;; -> (:pocket-watch :marmalade :door)
(cons 5 '()) ;; same as (cons 5 nil)
;; -> (5)
(cons 4 (cons 5 nil))
;; -> (4 5)
'(1 2 3 4 5)
;; -> (1 2 3 4 5)
(list 1 2 3 4 5)
;; -> (1 2 3 4 5)

Vectors

Vectors are for collections of data that you want to access anywhere by position.

to create vector, use square brackets

[:jar1 1 2 3 :jar2]
(first [:jar1 1 2 3 :jar2])
;; -> :jar1
(rest [:jar1 1 2 3 :jar2])
;; ->  (1 2 3 :jar2)
;; return a list

in vectors, you have fast index access to the elements:

(nth [:jar1 1 2 3 :jar2] 0)
;; -> :jar1
(nth [:jar1 1 2 3 :jar2] 2)
;; -> 2
(last [:rabbit :pocket-watch :marmalade])
;; -> :marmalade
(last (:rabbit :pocket-watch :marmalade))
;; -> :marmalade

vectors have better index access performance than lists, if you need to access the elements of collection by index, use a vector.

more common functions

(count [1 2 3 4])
;; -> 4
(conj [:toast :butter] :jam :honey)
;; -> [:toast :butter :jam :honey]
(conj '(:toast :butter) :jam :honey)
;; -> (:honey :jam :toast :butter)

conj adds to a collection in the most natural way for the data structure. for lists, it adds to the beginning. for vectors, it adds to the end.

Maps

Maps are for key-value pairs.

to create maps, use curly braces:

{:jam1 "strawberry" :jam2 "blackberry"}

maps are the one place that it can be idiomatic to leave the commas in for readability:

{:jam1 "strawberry", :jam2 "blackberry"}

get a value from maps

(get {:jam1 "strawberry", :jam2 "blackberry"} :jam2)
;; -> "blackberry"
;; get with a default value, put it as the last argument
(get {:jam1 "strawberry", :jam2 "blackberry"} :jam3 "not found")
;; -> "not found"

a more idiomatic way to get value from maps:

(:jam2 {:jam1 "strawberry" :jam2 "blackberry" :jam3 "marmalade"})
;; -> "blackberry"

when using keyword as key, the key itself is also a function.

get keys and values:

(keys {:jam1 "strawberry" :jam2 "blackberry" :jam3 "marmalade"})
;; -> (:jam3 :jam2 :jam1)
(vals {:jam1 "strawberry" :jam2 "blackberry" :jam3 "marmalade"})
;; -> ("marmalade" "blackberry" "strawberry")

(remember, collections are immutable, below you actually get a new version of collection)

to update value:

(assoc {:jam1 "red" :jam2 "black"} :jam1 "orange")
;; -> {:jam2 "black", :jam1 "orange"}

to remove value:

(dissoc {:jam1 "strawberry" :jam2 "blackberry"} :jam1)
;; -> {:jam2 "blackberry"}

merge:

(merge {:jam1 "red" :jam2 "black"}
    {:jam1 "orange" :jam3 "red"}
    {:jam4 "blue"})
;; -> {:jam4 "blue", :jam3 "red", :jam2 "black", :jam1 "orange"}

Sets

Sets are for collections of unique elements.

create a set with #{}

#{:red :blue :white :pink}
;; duplicate are not allowed
#{:red :blue :white :pink :pink}
;; -> error

common sets functions in clojure.set:

;; union
;; combine all
(clojure.set/union #{:r :b :w} #{:w :p :y})
;; -> #{:y :r :w :b :p}
;; difference
;; takes elements away from one of the sets
(clojure.set/difference #{:r :b :w} #{:w :p :y})
;; -> #{:r :b}
;; intersection
;; return only shared elements
(clojure.set/intersection #{:r :b :w} #{:w :p :y})
;; -> #{:w}

convert other collecions to sets:

(set [:rabbit :rabbit :watch :door])
;; -> #{:door :watch :rabbit}
(set {:a 1 :b 2 :c 3})
;; -> #{[:c 3] [:b 2] [:a 1]}

get element from a set:

(get #{:rabbit :door :watch} :rabbit)
;; -> :rabbit
(get #{:rabbit :door :watch} :jar)
;; -> nil
;; use keyword as a function
(:rabbit #{:rabbit :door :watch})
;; -> :rabbit
;; the set itself can be used as a function to do the same thing
(#{:rabbit :door :watch} :rabbit)
;; -> :rabbit

check if element exists:

(contains? #{:rabbit :door :watch} :rabbit)
;; -> true
(contains? #{:rabbit :door :watch} :jam)
;; -> false

add element to a set:

(conj #{:rabbit :door} :jam)
;; -> #{:door :rabbit :jam}

remove element from a set:

(disj #{:rabbit :door} :door)
;; -> #{:rabbit}

Symbols

Clojure symbols refer to values. when a symbol is evaluated, it returns the thing it refer to.

def gives something a name, so we can refer to it.

(def developer "Alice")
;; -> #'user/developer

def created a var object in the default namespace (user) for the symbol developer.

developer
;; -> "Alice"
user/developer
;; -> "Alice"

when you want to have a temporary var, use let.

let allows us to have bindings to symbols that are only available within the context of the let.

What happens in a let, stays in the let.

(def developer "Alice")
;; -> #'user/developer
(let [developer "Alice in Wonderland"]
    developer)
;; -> "Alice in Wonderland"

the bindings of let are in a vector form. It expects pairs of symbol and values.

(let [developer "Alice in Wonderland"
    rabbit "White Rabbit"]
    [developer rabbit])
;; -> ["Alice in Wonderland" "White Rabbit"]
  • use def to create global vars.
  • use let to create temporary bindings.

Creating a function

defn takes following arguments:

  • name of the function
  • a vector of parameters
  • body of the function
    (defn follow-the-rabbit [] "Off we go!")
    ;; -> #'user/follow-the-rabbit
    (follow-the-rabbit)
    ;; -> "Off we go!"
    (defn shop-for-jam [jam1 jam2]
        {:name "jam-basket"
        :jam1 jam1
        :jam2 jam2})
    ;; -> #'user/shop-for-jam
    (shop-for-jam "strawberry" "marmalade")
    ;; -> {:name "jam-basket", :jam1 "strawberry", :jam2 "marmalade"}

anonymous functions

;; return a function
(fn [] (str "Off we go" "!"))
;; invoke it directly
((fn [] (str "Off we go" "!")))
;; -> "Off we go!"
;; shorthand, use # in front of the parens:
(#(str "Off we go" "!"))
;; -> "Off we go!"
;; for parameters
;; if only 1 parameter, just use %
(#(str "Off we go" "!" " - " %) "again")
;; -> "Off we go! - again"
;; more than 1, just %1, %2 ...
(#(str "Off we go" "!" " - " %1 %2) "again" "?")
;; -> "Off we go! - again?"

defn is just the same as using def and binding the name to the anonymous function:

(def follow-again (fn [] (str "Off we go" "!")))
;; -> #'user/follow-again
(follow-again)
;; -> "Off we go!"

Namespaces

create a new namespace:

(ns alice.favfoods)

check current namespace:

*ns*

3 main ways of using libs in your namespace using require:

  1. use require expression with the namespace as argument.
    (require 'clojure.set)
  1. use require expression with an alias using :as.
    (require '[alice.favfoods :as af])
    ;; it's also common to see it nested within the ns:
    (ns wonderland
        (:require [alice.favfoods :as af]))
  1. use require with the namespace and the :refer :all options.
    (ns wonderland
        (:require [alice.favfoods :refer :all]))

however, the last way may create conflicts and also hard to read the code.

there's also a use expression that is the same as the require with :refer :all.

sum up

(ns wonderland
    (:require [clojure.set :as s]))
(defn common-fav-foods [foods1 foods2]
    (let [food-set1 (set foods1)
        food-set2 (set foods2)
        common-foods (s/intersection food-set1 food-set2)]
        (str "Common Foods: " common-foods)))
(common-fav-foods [:jam :brownies :toast]
    [:lettuce :carrots :jam])
;; -> "Common Foods: #{:jam}"

Chapter 2. Flow and Functional Transformations

expression and form

expression is code that can be evaluated for a result.

form is term for a valid expression that can be evaluated.

(first)

is an expression but not a valid form.

Controlling the Flow with Logic

(class true)
;; -> java.lang.Boolean
(true? true)
;; -> true
(true? false)
;; -> false
(false? false)
;; -> true
(false? true)
;; -> false
(nil? nil)
;; -> true
(nil? 1)
;; -> false
(not true)
;; -> false
(not false)
;; -> true
(not nil)
;; -> true

remember, nil is logically false in tests.

(= :drinkme :drinkme)
;; -> true
;; collection equality is special:
(= '(:drinkme :bottle) [:drinkme :bottle])
;; -> true
;; not= is a shortcut for (not (= x y))
(not= :drinkme :4)
;; -> true

Logic Tests on Collections

(empty? [:table :door :key])
;; -> false
(empty? [])
;; -> true
(empty? {})
;; -> true
(empty? '())
;; -> true

if we look at the definition of empty?, there is seq:

(defn empty?
    [coll] (not (seq coll)))

In Clojure, there are the collection and sequence abstractions.

The collections are simply a collection of elements.

The seq function turns the collection into a sequence.

A sequence is a walkable list abstraction for the collection data structure.

(empty? [])
;; -> true
(seq [])
;; -> nil

remember, use seq to check for not empty instead of (not (empty? x)). this is because nil is treated as logically false in tests.

;; every? takes a predicate and a collection as arguments
(every? odd? [1 3 5])
;; -> true
(every? odd? [1 2 3 4 5])
;; -> false

A predicate is a function that returns a value used in a logic test.

create a predicate:

(defn drinkable? [x]
    (= x :drinkme))
(every? drinkable? [:drinkme :drinkme])
;; -> true
;; use anonymous function
(every? (fn [x] (= x :drinkme)) [:drinkme :drinkme])
;; -> true
;; or
(every? #(= % :drinkme) [:drinkme :drinkme])
;; -> true

other logical test functions:

(not-any? #(= % :drinkme) [:drinkme :poison])
;; -> false
(not-any? #(= % :drinkme) [:poison :poison])
;; -> true
(some #(> % 3) [1 2 3 4 5])
;; -> true

some function can be used with a set to return the element, or the first matching element of the sequence.

;; set is a function of its own member
(#{1 2 3 4 5} 3)
;; -> 3
(some #{3} [1 2 3 4 5])
;; -> 3
(some #{4 5} [1 2 3 4 5])
;; -> 4

Harnessing the Power of Flow Control

if takes 3 parameters

  1. the expression that is the logical test.
  2. (if the test expression evaluates to true), evaluates the 2nd parameter.
  3. (if false), evaluates the 3rd parameter.
    (if true "it is true" "it is false")
    ;; -> "it is true"
    (if false "it is true" "it is false")
    ;; -> "it is false"
    (if nil "it is true" "it is false")
    ;; -> "it is false"
    (if (= :drinkme :drinkme)
        "Try it"
        "Don't try it")
    ;; -> "Try it"

combining let and if

(let [need-to-grow-small (> 5 3)]
    (if need-to-grow-small
        "drink bottle"
        "don't drink bottle"))
;; -> "drink bottle"

or just use if-let:

(if-let [need-to-grow-small (> 5 1)]
    "drink bottle"
    "don't drink bottle"))
;; -> "drink bottle"

if you only want to do one thing when test is true, use when

when takes a predicate, if it is logical true, it will evaluate the body. otherwise, it returns nil.

(defn drink [need-to-grow-small]
    (when need-to-grow-small "drink bottle"))
(drink true)
;; -> "drink bottle"
(drink false)
;; -> nil

there is also a when-let:

(when-let [need-to-grow-small true]
    ("drink bottle")
;; -> "drink bottle"
(when-let [need-to-grow-small false]
    ("drink bottle")
;; -> nil

when we want to test for mutiple things, use cond:

(let [bottle "drinkme"]
    (cond
        (= bottle "poison") "don't touch"
        (= bottle "drinkme") "grow smaller"
        (= bottle "empty") "all gone"))
;; -> "grow smaller"
        
in the cond clause, once a logical test returns true and the expression is evaluated, none of the other test clauses are tried.
(let [x 5]
    (cond
        (> x 10) "bigger than 10"
        (> x 4) "bigger than 4"
        (> x 3) "bigger than 3"))
;; -> "bigger than 4"

for returning a default value instead of nil, use :else

(let [bottle "mystery"]
    (cond
        (= bottle "poison") "don't touch"
        (= bottle "drinkme") "grow smaller"
        (= bottle "empty") "all gone"
        :else "unknown"))
;; -> "unknown"

case is shortcut for the cond where there is only one test value and it can be compared with an =:

(let [bottle "drinkme"]
    (case bottle
        "poison" "don't touch"
        "drinkme" "grow smaller"
        "empty" "all gone"))
;; -> "grow smaller"

however, case will throw an exception for no matching test. simply add an extra value as default:

(let [bottle "mystery"]
    (case bottle
        "poison" "don't touch"
        "drinkme" "grow smaller"
        "empty" "all gone"
        "unknown"))
;; -> "unknown"

Functions Creating Functions and Other Neat Expressions

partial is a way of currying in Clojure. Currying is a way to generate a new function with an argument partially applied.

(defn adder [x y]
    (+ x y ))
(adder 3 4)
;; -> 7
(defn adder-5 (partical adder 5))
(adder-5 10)
;; -> 15

comp creates a new function that combines other functions. It takes any number of functions as its parameters and returns the composition of those functions going from right to left.

(defn toggle-grow [direction]
    (if (= direction :small) :big :small))
(defn oh-my [direction]
    (str "Oh My! You are growing " direction))
(oh-my (toggle-grow :small))
;; -> "Oh My! You are growing :big"
;; or use comp
(defn surprise [direction]
    ((comp oh-my toggle-grow) direction))

Destructuring

destructuring allows you to assign named bindings for the elements in things like vectors and maps.

(let [[color size] ["blue" "small"]]
    (str "The " color " door is " size))
;; -> "The blue door is small"

the destructuring knew what values to bind by the placement of the symbols in the binding expression.

;; without destructuring:
(let [x ["blue" "small"]
    color (first x)
    size (last x)]
    (str "The " color " door is " size))
;; -> "The blue door is small"

to keep th initial data structure as a binding, use :as keyword:

(let [[color [size] :as original] ["blue" ["small"]]]
    {:color color :size size :original original})
;; -> {:color "blue", :size "small", ":original ["blue" ["small"]]}

destructuring can also be done with maps:

(let [{flower1 :flower1 flower2 :flower2}
    {:flower1 "red" :flower2 "blue"}]
    (str "The flowers are " flower1 " and " flower2))
;; -> "The flowers are red and blue"

specify default values with :or:

(let [{flower1 :flower1 flower2 :flower2 :or {flower2 "missing"}}
    {:flower1 "red"}]
    (str "The flowers are " flower1 " and " flower2))
;; -> "The flowers are red and missing"

:as works in maps too:

(let [{flower1 :flower1 :as all-flowers}
    {:flower1 "red"}]
    [flower1 all-flowers])
;; -> ["red" {:flower1 "red"}]

most of the time, you will want to give the same name to the binding as the name of the key, use :keys directive:

(let [{:keys [flower1 flower2]}
    {:flower1 "red" :flower2 "blue"}]
    (str "The flowers are " flower1 " and " flower2))
;; -> "The flowers are red and blue"

destructuring is also available to use on parameters while defining functions with defn:

(defn flower-colors [{:keys [flower1 flower2]}]
    (str "The flowers are " flower1 " and " flower2))
(flower-colors {:flower1 "red" :flower2 "blue"})
;; "The flowers are red and blue"))

The Power of Laziness

Clojure can also work with infinite lists.

(take 5 (range))
;; -> (0 1 2 3 4)

calling range returns a lazy sequence, you an specify an end for the range by passing it a parameter:

(range 5)
;; -> (0 1 2 3 4)
(class (range 5))
;; -> clojure.lang.LazySeq

but when you don't specify an end, the default is infinity.

repeat can be used to generate an infinite sequence of repeated items:

(repeat 3 "rabbit")
;; -> ("rabbit" "rabbit" "rabbit")
(class (repeat 3 "rabbit"))
;; -> clojure.lang.LazySeq

repeatedly takes a function that will be repeatedly executed over and over again.

(repeat 5 (rand-int 10))
;; -> (7 7 7 7 7)
(repeatedly 5 #(rand-int 10))
;; -> (1 5 8 4 3)

(take 10 (repeatedly #(rand-int 10)))

cycle takes a collection as an argument and returns a lazy sequence of the items in the collection repeated infinitely.

(take 3 (cycle ["big" "small"]))
;; -> ("big" "small" "big")

rest will return a lazy sequence when it operates on a lazy sequence:

(take 3 (rest (cycle ["big" "small"])))
;; -> ("small" "big" "small")

Recursion

(def adjs ["normal"
    "too small"
    "too big"
    "is swimming"])
(def alice-is [in out]
    (if (empty? in)
        out
        (alice-is
            (rest in)
            (conj out (str "Alice is " (first in))))))
(alice-is adjs [])

you can also do it with loop

(defn alice-is [input]
    (loop [in input
        out []]
        (if (empty? in)
            out
            (recur (rest in)
                (conj out (str "Alice is " (first in)))))))
(alice-is adjs)

recur jumps back to the recursion point, which is the beginning of the loop, and rebinds with new parameters.

using recur also has another very important advantage. It provides a way of not "consuming the stack" for recursive calls:

(defn countdown [n]
    (if (= n 0)
        n
        (countdown (- n 1))))
(countdown 3)
;; -> 0
(countdown 100000)
;; -> StackOverflowError

in the recursive call, a new frame was added to the stack for every function call, recur only needs one stack at a time.

(defn countdown [n]
    (if (= n 0)
        n
        (recur (- n 1))))
(countdown 100000)
;; -> 0

In general, always use recur when you are doing recursive calls.

The Functional Shape of Data Transformations

map

(def animals [:mouse :duck :dodo :lory :eaglet])
(map #(str %) animals)
;; -> (":mouse" ":duck" ":dodo" ":lory" ":eaglet")
(class (map #(str %) animals))
;; -> clojure.lang.LazySeq

map returns a lazy sequence.

doall forces evaluation of the side effets:

(def animal-print (doall (map #(println %) animals)))
;; -> do the println
(animal-print)
;; return value only but no println

map can also take more than one collection to map against. It uses each collection as a parameter to the function.

the map function will terminate when the shortest collection ends. because of this, we can even use an infinite list with it:

(def animals ["mouse" "duck" "dodo" "lory" "eaglet"])
(map gen-animal-string animals (cycle ["brown" "black"]))

reduce

differs from map in that you can change the shape of the result.

(reduce + [1 2 3 4 5])
;; -> 15
(reduce (fn [r x] (+ r (* x x))) [1 2 3])
;; -> 14

unlike map, you cannot reduce an infinite sequence (e.g., range)

filter

takes a predicate and a collection as an argument.

(filter (complement nil?) [:mouse nil :duck nil])
;; -> (:mouse :duck)

complement function takes the function and returns a function that takes the same arguments, but returns the opposite truth value.

((complement nil?) nil)
;; -> false

remove

takes a predicate and a collection.

(remove nil? [:mouse nil :duck nil])
;; -> (:mouse :duck)

for

(for [animal [:mouse :duck :lory]]
    (str (name animal)))
;; -> ("mouse" "duck" "lory")

the result is a lazy sequence.

if more than one collection is specified in the for, it will iterate over them in a nested fashion.

(for [animal [:mouse :duck :lory]
    color [:red :blue]]
    (str (name color) (name animal)))

:let modifier:

(for [animal [:mouse :duck :lory]
    color [:red :blue]
    :let [animal-str (str "animal-" (name animal))
        color-str (str "color-" (name color))
        display-str (str animal-str color-str)]]
    display-str)

:when modifier:

(for [animal [:mouse :duck :lory]
    color [:red :blue]
    :let [animal-str (str "animal-" (name animal))
        color-str (str "color-" (name color))
        display-str (str animal-str color-str)]
    :when (= color :blue)]
    display-str)

flatten

takes any nested collection and returns the contents in a single flattened sequence:

(flatten [[:duck [:mouse] [[:lory]]]])
;; -> (:duck :mouse :lory)

into

takes the new collection and returns all the items of the collection conj-ed on to it:

(into [] '(1 2 3))
;; -> [1 2 3]
(into (sorted-map) {:b 2 :a 1 :z3})
  
// vectors into maps
(into {} [[:a 1] [:b 2] [:c 3]])
// maps into vectors
(into [] {:a 1, :b 2, :c 3})

partition

partition is useful for dividing up collections:

(partition 3 [1 2 3 4 5 6 7 8 9 10])
;; -> ((1 2 3) (4 5 6) (7 8 9))
(partition-all 3 [1 2 3 4 5 6 7 8 9 10])
;; -> ((1 2 3) (4 5 6) (7 8 9) (10))

partition-by takes a function and applies it to every element in the collection. it creates a new partition every time the result changes:

(partition-by #(= 6 %) [1 2 3 4 5 6 7 8 9 10])
;; -> ((1 2 3 4 5) (6) (7 8 9 10))

Chapter 3. State and Concurrency

Using Atoms for independent Items

Atoms are designed to store the state of something that is independent, meaning we can change the value of it independently of changing any other state.

(def who-atom (atom :caterpillar))

to see the value of the atom, need to dereference it with a preceding @:

who-atom
;; -> #<Atom@....: :caterpillar>
@who-atom
;; -> :caterpillar

changes to atom are always made synchronously.

first is using reset!, replaces old value with new value and returns the new value:

(reset! who-atom :chrysalis)
@who-atom
;; -> :chrysalis

the other way is with swap!, applies a function on the old value and sets it to the new value:

(def change [state]
  (case state
    :caterpillar :chrysalis
    :chrysalis :butterfly
    :butterfly))
(swap! who-atom change)
@who-atom
;; -> :chrysalis
(swap! who-atom change)
@who-atom
;; -> :butterfly

when using swap!, the function used must be free of side effects.

swap! operator reads the value of the atom and applies the function on it, then compares the current value of the atom again to make sure that another thread hasn't changed it. if the value has changed in the meantime, it will retry.

this means any side effects in functions might be executed multiple times.

(def counter (atom 0))
@counter
;; -> 0
;; the understore `_` is the name of the value, we don't care here.
(dotimes [_ 5] (swap! counter inc))
@counter
;; -> 5

to use multiple threads on this, use the future form. the future form takes a body and executes it in another thread.

(let [n 5]
  (future (dotimes [_ n] (swap! counter inc)))
  (future (dotimes [_ n] (swap! counter inc)))
  (future (dotimes [_ n] (swap! counter inc))))
@counter
;; -> 15

if we use a function with side effect:

(defn inc-print [val]
  (println val)
  (inc val))
(let [n 2]
  (future (dotimes [_ n] (swap! counter inc-print)))
  (future (dotimes [_ n] (swap! counter inc-print)))
  (future (dotimes [_ n] (swap! counter inc-print)))

you can see some values printed multiple times.

Using Refs for Coordinated Changes

What if we have more than one thing that needs to change in a coordinated fashion? refs allows coordinated shared state.

that makes them different from atoms is that you need to change their values within a transaction. Clojure uses software transactional memory (STM) to accomplish this.

All actions on refs within the transaction are:

  • Atomic: updates will occur to all the refs, if something goes wrong, none of them will be updated.
  • Consistent: optional validator function can be used to check value before the transaction commits.
  • Isolated: transaction has its own isolated view of the world, it will not see any effects from other transactions.
(def alice-height (ref 3))
(def right-hand-bites (ref 10))

like atoms, use a preceding @ to dereference refs:

@alice-height
;; -> 3

alter form takes a ref and a function to apply to the current value (similar to swap!)

(defn eat-from-right-hand []
  (when (pos? @right-hand-bites)
    (alter right-hand-bites dec)
    (alter alice-height #(+ % 24))))
(eat-from-right-hand)
;; -> IllegalStateException No transaction running

we need to run this in a transaction, we do this by using a dosync form. it will coordinate any state changes within the form in a transaction.

(dosync (eat-from-right-hand))
;; -> 27
@alice-height
;; -> 27
@right-hand-bites
;; -> 9

try it with multiple threads:

(let [n 2]
  (future (dotimes [_ n] (eat-from-right-hand)))
  (future (dotimes [_ n] (eat-from-right-hand)))
  (future (dotimes [_ n] (eat-from-right-hand))))
@alice-height
;; -> 147
@right-hand-bites
;; -> 4

the function of the alter must be side-effect free too, the reason is the same: there would be retries.

there is another function called commute that we could use instead of alter, it also must be called in a transaction.

the difference between them is commute will not retry during the transaction. instead, it will use an in-transaction-value in the meantime, finally setting the ref value at the commit point in the transaction.

this feature is very nice for speed and limiting retries.

on the other hand, the function that commute applied must be commutative (execute order does not matter, like addition), or have a last-one-in-wins behavior.

the example above can use commute instead of alter.

Transactions that involve time-consuming computations and a large number of refs are most likely to be retried. If you are looking to limit retries, this is a reason you might prefer an atom with a map of state over many refs.

when you have one ref what is defined in relation to another ref, use ref-set instead of alter:

(def x (ref 1))
(def y (ref 1))
(defn new-values []
  (dosync
    (alter x inc)
    (ref-set y (+ 2 @x)))) ;; use ref-set
(let [n 2]
  (future (dotime [_ n] (new-values)))
  (future (dotime [_ n] (new-values))))
@x
;; -> 5
@y
;; -> 7

Using Agents to Manage Changes on Their Own

atoms and refs are synchronous, agents are used for independent and asynchronous changes.

if you don't need the result right away, you can pass it to an agent for processing.

(def who-agent (agent :caterpillar))
@who-agent
;; -> caterpillar

we can change the state of an agent by using send.

(def change [state]
  (case state
    :caterpillar :chrysalis
    :chrysalis :butterfly
    :butterfly))
(send who-agent change)
@who-agent
;; -> :chrysalis

unlike swap! and alter, send returns immediately.

there is another way to dispatch an action to the agent, with send-off. the difference is send-off should be used for potentially I/O-blocking actions.

send uses a fixed thread pool, good for CPU-bound operations. send-off uses an expandable thread pool necessary to avoid an I/O-bound thread pool from blocking:

agents can also handle transactions with their action, means we could change refs within an agent action, or send actions only if the transaction commits.

when there's an Exception:

(def who-agent (agent :caterpillar))
(defn change [state]
  (case state
    :caterpillar :chrysalis
    :chrysalis :butterfly
    :butterfly))
(defn change-error [state]
  (throw (Exception. "Boom!")))
(send who-agent change-error)
;; -> failed
@who-agent
;; -> :caterpillar

the agent's state did not change. the agent itself is now failed.

The exception has been cached, and next time an action is processed, the agent's error will be thrown:

(send-off who-agent change)
;; -> Exception Boom!

you can inspect agent's erorr with agent-errors:

(agent-errors who-agent)
;; -> Exception Boom!

the agent will stay in this failed state until the agent is restarted with restart-agent, which clears its errors and resets the state of the agent:

(restart-agent who-agent :caterpillar)
;; -> :caterpillar
(send who-agent change)

@who-agent
;; -> :chrysalis

to control how the agent responds to errors, use set-error-mode!, it can be set to either :fail or :continue:

(set-error-mode! who-agent :continue)

If it is set to :continue and we assign an error handler with the set-error-handler-fn! function, the error handler will happen on an agent exception, but the agent itself will continue on without a need for a restart:

(defn err-handler-fn [a ex]
  (println "error " ex " value is " @a))
(set-error-mode! who-agent :continue)
(set-error-handler! who-agent err-handler-fn)
(send who-agent change-error)
;; -> print out
@who-agent
;; -> :caterpillar
;; however the agent will continue on without a restart for the next call
(send who-agent change)
@who-agent
;; -> :chrysalis

to sum up:

| Type | Communication | Coordination |
|-
| Atom | Synchronous | Uncoordinated |
| Ref | Synchronous | Coordinated |
| Agent | Asynchronous | Uncoordinated |

Chapter 4. Java Interop and Polymorphism

Clojure uses the new and . special form to interact with Java classes.

String cString = new String("caterpillar");
cString.toUpperCase();

clojure's equivalent:

(. "caterpillar" toUpperCase)

or

(.toUpperCase "caterpillar")

if the jave method takes arguments, they are included after the object.

String c1String = new String("caterpillar");
String c2String = new String("pillar");
c1String.indexOf(c2);

clojure's equivalent:

(.indexOf "caterpillar" "pillar")

in clojure, the first argument is the string we want to call the method on, and the second is the argument.

use new to create instance of Java object:

(new String "Hi!!")

Another way to create an instance of a Java class is to use a shorthand form by using a dot right after the class name:

(String. "Hi!!")

to import Java classes, do it by using :import in the namespace form:

(ns caterpillar.network
  (:import (java.net InetAddress)))

to execute static methods on java class, use a forward slash (/):

(InetAddress/getByName "localhost")

to get a property off of an object, use the dot notation:

(.getHostName (InetAddress/getByName "localhost"))

we can also use full qualified names without importing:

(java.net.InetAddress/getByName "localhost")

there is also a doto macro, which allows us to take a java object and then act on it with a list of operations:

(def sb (doto (StringBuffer. "Who ")
          (.append "are ")
          (.append "you?")))
(.toString sb)
;; -> "Who are you?"

without doto:

(def sb
  (.append
    (.append
      (StringBuffer. "Who ")
    "are ")
  "you?"))

Practical Polymorphism

(defn who-are-you [input]
  (cond
    (= java.lang.String (class input)) "String - Who are you?"
    (= clojure.lang.Keyword (class input)) "Keyword - Who are you?"
    (= java.lang.Long (class input)) "Number - Who are you?"))
(who-are-you :alice)
;; -> Keyword
(who-are-you "alice")
;; -> String
(who-are-you 123)
;; -> Number
(who-are-you true)
;; -> nil

we can express this with polymorphism in clojure with multimethods.

first specifies how it is going to dispatch. that is, how it is going to decide which of the following methods to use:

(defmulti who-are-you class)
(defmethod who-are-you java.lang.Keyword [input]
  (str "String - Who are you? " input))
(defmethod who-are-you clojure.lang.Keyword [input]
  (str "Keyword - Who are you? " input))
(defmethod who-are-you java.lang.Long [input]
  (str "Number - Who are you? " input))
(who-are-you :alice)
;; -> Keyword
(who-are-you "alice")
;; -> String
(who-are-you 123)
;; -> Number
(who-are-you true)
;; Exception

we can also provide a default dispatch method using the :default keyword:

(defmethod who-are-you :default [input]
  (str "I don't know - who are you?" input))
(who-are-you true)
;; -> I don't know ...

any function can be given to dispatch on. we can even inspect the value of a map as input:

(defmulti eat-mushroom (fn [height]
                          (if (< height 3)
                            :grow
                            :shrink)))
(defmethod eat-mushroom :grow [_]
  "Eat the right side to grow.")
(defmethod eat-mushroom :shrink [_]
  "Eat the left side to shrink.")
(eat-mushroom 1)
;; -> ... grow
(eat-mushroom 9)
;; -> ... shrink

Another way to use polymorphism in clojure is to use protocols.

protocols can handle polymorphism elegantly with groups of functions.

(defprotocol BigMushroom
  (eat-mushroom [this]))

the parameter this is the thing that we are going to perform the function on:

(extend-protocol BigMushroom
  java.lang.String
  (eat-mushroom [this]
    (str (.toUpperCase this) " mmm tasty!"))
  clojure.lang.Keyword
  (eat-mushroom [this]
    (case this
      :grow "Eat the right side!"
      :shrink "Eat the left side!"))
  java.lang.Long
  (eat-mushroom [this]
    (if (< this 3)
      :grow "Eat the right side to grow!"
      :shrink "Eat the left side to shrink!")))
(eat-mushroom "Big Mushroom")
;; -> "BIG MUSHROOM mmm tasty!"
(eat-mushroom :grow)
;; -> "Eat the right side!"
(eat-mushroom 1)
;; -> "Eat the right side to grow!"

we are using protocols to add methods to existing data structure. what if we want to add our own data structure?

Clojure's answer to this is data types.

there're two solutions:

if you need structured data, use defrecord, which creates a class with a new type.

if you just need an object with a type to save memory, use deftype.

defrecord

the defrecord form defineds the fields that the class will hold:

(defrecord Mushroom [color height])
;; -> caterpillar.network.Mushroom

now can create new object with a dot notation:

(def regular-mushroom (Mushroom. "white and blue polka dots" "2 inches"))
(class regular-mushroom)
;; -> caterpillar.network.Mushroom

we can get the values with the dot-dash that is preferred over the dot-prefix for accessing fields:

(.-color regular-mushroom)
;; -> "white ..."
(.-height regular-mushroom)
;; -> "2 inches"

we can combine structured data type with protocols to implement interfaces:

(defprotocol Edible
  (bite-right-side [this])
  (bite-left-side [this]))

implement the protocol:

(defrecord WonderlandMushroom [color height]
  Edible
  (bite-right-side [this]
    (str "The " color " bite makes you grow bigger"))
  (bite-left-side [this]
    (str "The " color " bite makes you grow smaller")))

then another data type implements Edible:

(defrecord RegularMushroom [color height]
  Edible
  (bite-right-side [this]
    (str "The " color " bite tastes bad"))
  (bite-left-side [this]
    (str "The " color " bite tastes bad too")))

construct our mushroom objects:

(def alice-mushroom (WonderlandMushroom. "blue dots" "3 inches"))
(def reg-mushroom (RegularMushroom. "brown" "1 inch"))
(bite-right-side alice-mushroom)
;; -> "The blue dots bite makes you grow bigger"
(bite-right-side reg-mushroom)
;; -> "The brown bite tastes bad"

A real-world example of protocols is implementing different types of persistence. which is one data type writes to different types of data sources, one to database and one to s3 bucket for example.

deftype

sometimes we don't really care about the structure or the map lookup features provided by defrecord, we just need an object with a type to save memory.

in this case we should reach for deftype:

(defprotocol Edible
  (bite-right-side [this])
  (bite-left-side [this]))
(deftype WonderlandMushroom []
  Edible
  (bite-right-side [this]
    (str "The bite makes you grow bigger"))
  (bite-left-side [this]
    (str "The bite makes you grow smaller")))
(deftype RegularMushroom []
  Edible
  (bite-right-side [this]
    (str "The bite tastes bad"))
  (bite-left-side [this]
    (str "The bite tastes bad too")))
(def alice-mushroom (WonderlandMushroom.))
(def reg-mushroom (RegularMushroom.))
(bite-right-side alice-mushroom)
;; -> "The bite makes you grown bigger"
(bite-right-side reg-mushroom)
;; -> "The bite tastes bad"

the main difference between using protocols with defrecord and deftype is how you want your data organized.

if you want structure data, choose defrecord, otherwise, use deftype.

Caution

think before you use protocols.

sometimes you could have more simple solution without using protocols.

(def bite-right-side [mushroom]
  (if (= (:type mushroom) "wonderland")
    "The bite makes you grow bigger"
    "This bite tastes bad"))
(bite-right-side {:type "wonderland"})
;; -> "The bite makes you grown bigger"
(bite-right-side {:type "regular"})
;; -> "The bite tastes bad"

Chapter 5. How to Use Clojure Projects and Libraries

to create a new project skeleton:

$ lein new serpent-talk

for namespace and filename, dashes are not being valid in java class names.

so always use understores for directories and filenames, and use dashes for namespaces.

show dependencies as tree:

$ lein deps :tree

run main function of a namespace from the command line using lein run -m:

$ lein run -m serpent-talk.talk "Hello pigeon"

you can also add this to project.clj file specifying the main function:

:main serpent-talk.talk

then can run with:

$ lein run "Hello pigeon"

Chapter 6. Communication with core.async

create a new project with core.async

$ lein new async-tea-party

add dependency to project.clj:

:dependencies [[org.clojure/clojure "1.6.0"]
               [org.clojure/core.async "xxx"]]

include core.async in the namespace (src/async_tea_party/core.clj file):

(ns async-tea-party.core
  (:require [clojure.core.async :as async]))

Basics of core.async Channels

create a channel:

(def tea-channel (async/chan))

there are two main ways that you get things on and off channels: synchronously and asynchronously.

  • >!!: a blocking put, puts data on the channel synchronously.
  • <!!: a blocking take, takes data off the channel synchronously.
  • >!: an async put, puts data on the channel asynchronously, needs to be used with a go block.
  • <!: an async take, takes data off the channel asynchronously, needs to be used with a go block.

so when you see !! it means a blocking call.

the tea-channe created above is an unbuffered channel, the main thread would block until it got taken off. (it will also lock up REPL)

to create a buffered channel:

(def tea-channel (async/chan 10))

now test with the blocking put:

(async/>!! tea-channel :cup-of-tea)
;; -> true

get it off with a blocking take:

(async/<!! tea-channel)
;; -> :cup-of-tea

to close a channel:

(async/close! tea-channel)

this closes the channel to new inputs, however, if there are still values on it, they can be taken off. when the channel is finally empty, it will return a nil.

(async/>!! tea-channel :cup-of-tea-2)
(async/>!! tea-channel :cup-of-tea-3)
(async/>!! tea-channel :cup-of-tea-4)
(async/close! tea-channel)
;; -> nil
;; new puts will fail
(async/>!! tea-channel :cup-of-tea-5)
;; -> false
;; but existing values can be taken
(async/<!! tea-channel)
;; -> :cup-of-tea-2
(async/<!! tea-channel)
;; -> :cup-of-tea-3
(async/<!! tea-channel)
;; -> :cup-of-tea-4
;; until it's empty
(async/<!! tea-channel)
;; -> nil

also nil is special, you can not put it on a channel:

(async/>!! tea-channel nil)
;; Exception

so nil lets us know that the channel is empty.

now try async:

(let [tea-channel (async/chan)]
  (async/go (async/>! tea-channel :cup-of-tea-1))
  (async/go (println "Thanks for the " (async/<! tea-channel))))
;; Will print to stdout:
;; Thanks for the :cup-of-tea-1

Chapter 7. Creating Web Applications with Clojure

Chapter 8. The Power of Macros

Chapter 9. Joining the Clojure Community

Chapter 10. Weekly Living Clojure Training Plan

Chapter 11. Further Adventures

Search Blog: