Clojure introduction

Simple examples and more

Agenda

  • History
  • Language Basics
    • live coding
  • Libraries and Links to get started

History Clojure

  • clojure.org
  • Lisp based language
  • released 2007
  • inventor Rich Hickey
  • current version 1.8
  • host language
    • Clojure for JVM
    • Clojurescript for V8
    • ClojureCLR for .NET

Lisp history

  • second-oldest high-level language (1958)
  • Fortran 1957
  • List Processing
  • inventor John McCarthy

Lisp inventions

  • Homoiconicity (Code is data. Data is code)
  • macros
  • REPL (Read-Eval-Print-Loop)
  • if-then-else
  • recursive functions
  • automatic garbage collection
  • generic functions
  • lexical closures

Lisp quotes

“ the greatest single programming language ever designed ”
- Alan Kay, on Lisp
“ Lisp is worth learning for the profound enlightenment experience you will have when you finally get it; that experience will make you a better programmer for the rest of your days, even if you never actually use Lisp itself a lot. ”
- Eric Raymond, “How to Become a Hacker

Clojure overview

  • strongly, dynamically typed
  • JVM hosted
  • Lisp
  • Functional programming language
  • Immutable data structures
  • REPL-Driven-Development
  • Concurrency
  • Homoiconicity (macros)

Functions


;; this is a comment

;; creates a global var
(def x 1)

(def hello-1 (fn []
               "Hello World!"))


(def hello-2 #(str "Hello " %))

;; creates a global var with a function as value
(defn hello-3
  "Returns hello world"
  []
  "Hello World!")

(defn hello-4
  "Multi arity function."
  ([] "Hello world!")
  ([name] (str "Hello " name "!")))

		        

Immutable data structures



;; data structures

(def a-vector [1 2 3])
(def another-vector (vector 1 2 3))
(conj a-vector 4)

(def a-list '(1 2 3))
(def another-list (list 1 2 3))
(conj a-list 4)

(def a-set #{1 2 3})
(def another-set (set (list 1 2 3 1)))
(conj a-set 4)

(def a-map {:foo "bar" :a 1})
(def another-map (hash-map :foo 1 :bar 2))
(assoc a-map :b 2)

;; (first a-vector)
;; (rest a-vector)
;; (drop 2 a-vector)
;; (pop a-vector)
;; (peek a-vector)
;; (into {:c "value"} a-map)
;; (into [5 6 7] a-vector)
;; (concat [1 2] [3 4])

(def users [{:name "James" :age 26}  {:name "John" :age 43}])
;; update the age of the second (index 1) user
(assoc-in users [1 :age] 44)

		        

Old Backend bug


// 2 dates in a domain class
Calendar scheduledArrivalTime;
Calender expectedArrivalTime;

Calendar cal = getArrivalTimeFromRemote() // Calender.getInstance()

// set arrivalTimes
scheduledArrivalTime = cal
expectedArrivalTime = cal;

// many, many lines of code later......

// somewhere in the code: 2 minutes delay for expectedArrivalTime
expectedArrivalTime.add(Calendar.MINUTE, 2);

// happy debugging :)
		    
  • in Java: prefer immutable
    • thread-safe
    • easier debugging ==> less surprises
  • Java8 Date and Time

More functions



(defn add-1
  "Sum up given parameters."
  [& more]
  (apply + more))

(defn destruct-1
  "Extract firstname and lastname and returns fullname."
  [{:keys [:firstname :lastname]}]
  {:fullname (str firstname " " lastname)})

(defn destruct-2
  "Extract firstname and lastname but also add defaults if one of the
  names do not exist. Returns concatenated fullname string."
  [{:keys [:firstname :lastname]
    :or {:firstname "John" :lastname "Doe"}}]
  {:fullname (str firstname " " lastname)})
		    

Conditionals


;; if and cond

(defn my-even?
  "Returns true if number is even."
  [n]
  (if (even? n)
    true
    false))

(defn greeting
  "Returns the greeting in the given language. Defaults to :english"
  ([] "Hello with with default argument")
  ([language]
   (cond (= language :french) "Bonjour"
         (= language :spanish) "Hola"
         :else "Hello")))

		    

Local variables


;; let

(defn area-circle
  "Returns the area of a circle with the given radius"
  [radius]
  (let [pi 3.1415
        radius-squared (* radius radius)]
    (* pi radius-squared)))

		    

Loops


;; loops

(defn factorial
  "Returns the factorial of the given number."
  [n]
  (loop [i n
         acc 1]
    (if (zero? i)
      acc
      (recur (dec i) (* acc i)))))

(defn print-list
  "Prints all entries of given list"
  [collection]
  (doseq [entry collection]
    (println entry)))
		    
  • loops not good lisp style
  • use map filter reduce

Lazy data structures


;; lazy data structures
(def natural-numbers (iterate inc 1))
;; (take 5 natural-numbers)

;; (inc (inc (inc 1)))
;; (-> 1 inc inc inc)
		    

Functional programming


;; map filter reduce
(def even-numbers (filter even? natural-numbers))

(defn sum-of-squares
  "Returns the sum of the given numbers squared."
  [& numbers]
  (reduce + 0  (map (fn [x]
                      (* x x))
                    numbers)))

;; partial
(def even-numbers-2 (iterate (partial + 2) 0))
;; (take 10 even-numbers-2)
		        

Functional programming part 2


(def fifth (comp first rest rest rest rest))

(defn fnth
  "Returns a function which returns n-th item from given sequence."
  [n]
  (apply comp (cons first (take (dec n) (repeat rest)))))

;; ((fnth 5) [1 2 3 4 5 6])


(defn fib [n]
  (condp = n
    0 1
    1 1
    (+ (fib (dec n)) (fib (- n 2)))))

(def memo-fib
  (memoize (fn [n]
             (condp = n
               0 1
               1 1
               (+ (memo-fib (dec n)) (memo-fib (- n 2)))))))
                        

Concurrency


;; concurrency refs (in-memory transactions)
(def account-a (ref 1000))
(def account-b (ref 0))

(defn transfer-money
  "Transfers the given amount of money from account a to account b"
  [amount a b]
  (dosync
   (alter a #(- % amount))
   (alter b #(+ % amount))))
;; @account-a
;; @account-b
;; (transfer-money 100 account-a account-b)
		    

Design by contract


;; pre- and post conditions
;; design-by-contract
(defn constrained-sqr [x]
  {:pre  [(pos? x)]
   :post [(> % 16), (< % 225)]}
  (* x x))
		    

Macros

“ If you give someone Fortran, he has Fortran. If you give someone Lisp, he has any language he pleases. ”
- Guy Steele (Programming Language guru, contributed to Java Language Spec, Scheme, ECMAScript, Common Lisp, C, co-auther of emacs)
“ First rule of macros: don't write macros. ”
- anonymous

Java for-each


// java 1.4 and before

for (int i = 0; i < list.size(); i++) {
    String s = (String) list.get(i);
    // do something with list item...
}

// java 1.5  (8 year later)

for (String s : list) {
    // do something with list item
}
		        

for-each macro


;; for-each macro
(defmacro for-each
  "Simulates the java for-each loop"
  [[sym coll] & body]
  `(loop [coll# ~coll]
     (when-let [[~sym & xs#] (seq coll#)]
       ~@body
       (recur xs#))))

;; (for-each [x [1 2 3]]
;;           (println x))

		        

More macros


;; infix macro
(defmacro infix
  "Use this macro to get a familiar syntax."
  [infixed]
  (list (second infixed) (first infixed) (last infixed)))

;; (infix (1 + 2))
;; (macroexpand-1 '(infix (1 + 2)))

(defmacro unless-1
  "Inverted if"
  [test & branches]
  (conj branches (list 'not test) 'if))

;; (unless-1 (= 1 1)
;;         true
;;         false)

(defmacro unless-2
  "Inverted if, easier"
  [test & branches]
  `(if (not ~test)
     ~@branches))

(defmacro my-and
  ""
  ([] true)
  ([x] x)
  ([x & next]
   `(let [and# ~x]
      (if and# (my-and ~@next) and#))))

;; (my-and true false)
;; (pprint (macroexpand-1 '(my-and true false)))
		        

More language features

Clojure Libraries

Clojurescript Libraries

THE END

- Source code & documentation

References

- Joy of Clojure - Michael Fogus and Chris Houser

- Programming Clojure - Stuart Halloway and Aaron Bedra

- Clojure Programming - Chas Emerick, B. Carper, C. Grand