Introduction to Clojure

Philip Potter and Tom Hall

@philandstuff and @thattommyhall

Meta

http://www.philandstuff.com/slides/2014/intro-to-clojure.html

Structure

About ⅓ presentation, ⅔ practical, 2 hours

First hour: language fundamentals

Second hour: project

LightTable

http://www.lighttable.com/

Start a Clojure Instarepl

Type Ctrl-<SPACE>

then type "instarepl"

press <ENTER>

Some useful shortcuts

Ctrl-D
Toggle Clojure documentation for symbol at cursor

Lisp

Basic syntax

Basic expression is an operator applied to zero or more arguments:

(operator arg1 arg2 ...)

Sometimes known as an s-expression or sexp.

Function calls

Python:

sorted([9,1,6,4])

Clojure:

(sort [9 1 6 4])

Methods

Ruby:

"".empty?
"foo".empty?

Clojure:

(empty? "")
(empty? "foo")

Methods with arguments

Ruby:

[1,2,3].concat( [4,5,6] )

Clojure:

(concat [1 2 3] [4 5 6])

Numerical operators

Ruby:

1 + 2
1 + 2 + 3 + 4

Clojure:

(+ 1 2)
(+ 1 2 3 4)

Control flow

1.upto(10).each do |x|
  puts 2*x
end
(for [i (range 1 10)]
  (* 2 i))

Defining functions

def add1(n)
  n + 1
end
(defn add1 [n]
  (+ n 1))

Your own syntax…

(defroutes my-app
  (GET "/" []
    "Welcome!")
  (GET "/profile/:user" [user]
    (str "Hello, " user "!"))
  (POST "/profile/:user" [user comment]
    (add-to-comments user comment))
  (route/not-found "<h1>Page not found</h1>"))

Your own syntax…

(go-loop [[response-ch request] (<! http-channel)]
  (condp = (:method request)
    :get  (>! response-ch "Hello world!")
    :post (do
            (>! missile-handler :launch)
            (>! response-ch "Missile launch sequence initiated."))))

Functional programming

Data

http://clojure.org/data_structures

Simple data types

nil

1 1/2 1234N 0.3M

"strings"

\c \h \a \r \s \!

:keyword 'symbol

Vectors

[:a :b :c]

(def v [:a :b :c])

(first v) ;=> :a

(last v) ;=> :c

(nth v 1) ;=> :b

Vectors

(def v [:a :b :c])

(conj v :d) ;=> [:a :b :c :d]

(assoc v 1 42) ;=> [:a 42 :c]

(pop v) ;=> [:a :b]

v ;=> [:a :b :c] -- unchanged!

Maps

{:foo 1, :bar 2}

(def m {:few 2, :some 4})

(get m :few) ;=> 2

(get m :many) ;=> nil

Sets

#{:a :b :c :d}

(def colours #{:red :yellow :blue})

(conj colours :green) ;=> #{:red :blue :green :yellow}

(disj colours :red) ;=> #{:blue yellow}

Functions

(fn [x y]
  (if (< x y)
    y
    x))
((fn [x y]
    (if (< x y)
      y
      x))
 12 56) ;=> 56

Defining functions

(def my-max
  (fn [x y]
    (if (< x y)
      y
      x)))

(my-max 12 56) ;=> 56

defn shorthand

(defn my-max [x y]
  (if (< x y)
    y
    x))

(my-max 12 56) ;=> 56

Local names

(let [triple (fn [x] (+ x x x))
      x      5]
  (triple x))
;;=> 15

(triple 10)
;; unable to resolve symbol: triple

Clojurescript koans

http://clojurescriptkoans.com

You may find the Clojure Grimoire useful:

http://grimoire.arrdem.com/

reference types & concurrency

What's more fundamental?

Classes, objects, locks, threads, variables, inheritance, encapsulation, dispatch…

Identity, time, values, state, place, perception, visibility…

Safety

Objects are not thread safe by default

We have to work hard to achieve safety

Safety

Lost updates:

  1. A reads: [:jam]
  2. B reads: [:jam]
  3. B writes [:jam :peanut-butter]
  4. A writes [:jam :nutella]

Safety

Immutable values and pure functions are safe by default

But: not all things we want to talk about are immutable values or pure functions

Definitions (From Rich Hickey, "Are We There Yet?")

Value
An immutable magnitude, quantity, number, or immutable composite thereof (eg vectors, maps, strings)
Identity
A putative entity we associate with a series of causally related values
Time
Relative ordering of causal values
State
A value of an identity at a point in time

Atoms

(atom init-val)
creates an atom
(deref a) or @a
gets current value
(swap! a f x y z)
replaces the current value with (f @a x y z) atomically

Atoms

(def a (atom [:jam]))
(swap! a conj :peanut-butter)
(swap! a conj :nutella)

Software Transactional Memory

(ref init-val)
creates a ref
(deref r) or @r
gets current value
(dosync ...)
performs a transaction over one or more refs

Software Transactional Memory

Within a transaction:

(ensure r)
coordinated read of a ref
(alter r f x y z)
replace current value of r with (f @r x y z)
(ref-set r x)
set value of r to x

Software Transactional Memory

If a transaction fails due to a concurrent modification, it automatically retries.

Software Transactional Memory

(def player  (ref #{}))
(def monster (ref #{:sword :gold :shield}))

(defn steal-from [thief victim]
  (dosync
    (let [chosen-item (first (shuffle (ensure victim)))]
      (alter victim disj chosen-item)
      (alter thief  conj chosen-item))))

(steal-from player monster)

Or are atoms enough?

(def world
  (atom {:player  #{}
         :monster #{}}))

(defn steal-from [world thief victim]
  (let [chosen-item (first (shuffle victim))]
    (-> world
        (update-in [victim] disj chosen-item)
        (update-in [thief]  conj chosen-item))))

(swap! world steal-from :player :monster)

Other concurrency types

Agents: uncoordinated, asynchronous

core.async: CSP-style (ie golang-style), channels and queues

Vars: thread-local storage

Java Interoperability

Syntax

;; import a class (at the repl)
(import 'java.net.URI)

;; new URI("http://icanhazip.com")
(def uri (URI. "http://icanhazip.com"))

;; call a static method
(def uri2 (URI/create "http://icanhazip.com"))

;; call a method
(.getScheme uri)
;;=> "http"

;; Java null maps to Clojure nil
(.getFragment uri)
;;=> nil

Dealing with mutable objects

(def l (ArrayList.))
(.add l 1)
(.add l 3)
(.add l "foobar")
;;=> l is now [1 3 "foobar"]
(def l
  (doto (ArrayList.)
    (.add 1)
    (.add 3)
    (.add "foobar")))

Clojure collections + Java functions

;; List's addAll() method takes a java.util.Collection:

(.addAll l [:more :data :from :clojure])

;; now l is [1 3 "foobar" :more :data :from :clojure]

Java collections + Clojure functions

(def l (doto (ArrayList.) (.addAll [1 3 "foobar"])))

(map str l)
;;=> ("1" "3" "foobar")

(filter number? l)
;;=> (1 3)

Projects and dependencies

This is all very neat, but how do I actually get something done?

Leiningen

Do you already have this installed? If not, go to http://leiningen.org/

Project

git clone git@github.com:WWCLondon/snake.git

Pick a team name, and:

git checkout -b <team-name>

Open snake/project.clj in LightTable:

References

Rich Hickey, "Are we there yet? "http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey

ClojureScript Koans: http://clojurescriptkoans.com/

London Clojurians: http://londonclojurians.org/

ClojureBridge: http://www.clojurebridge.org/

Events

(all announced on london-clojurians mailing list)

  • London Clojure Dojo
    • (next: Tuesday 30th September at ThoughtWorks)
    • (then: Monday 13th October at uSwitch)
  • London Clojure User Group at Skillsmatter
    • (next: Tuesday 2nd October)
  • Clojure eXchange 2014 (2-day clojure conference)
    • Thu 4th, Fri 5th December
  • Clojure Bridge Edinburgh, 26th September
    • (still spaces if you know anyone!)