Introduction to Clojure
What is Clojure
Clojure is a …
functional
immutable by default
concurrency ready
dynamically typed
young (2007)
opinionated
LISP
for the JVM
(but also for JavaScript and .NET/CLR)
What is it used for?
Datomic
a time-aware, fully ACID database system
Puppet
PuppetDB and Trapperkeeper
Overtone
a live / collaborative music synthesis environment
sayHEY
a crypto messenger (developed by us) with a ClojureScript web client
Netflix, eBay, Daily Mail, …
Functional
Functional
Functions are first-class citizens
They can be passed around, composed, generated, …
Functional
For loop, imperative
var xs = [1, 2, 3]; var result = []; for (var i = 0; i < xs.length; i++) { result.push(10 * xs[i]); } result // => [10, 20, 30]
Functional
Map, functional
var xs = [1, 2, 3]; var result = xs.map(function(x) { return 10 * x }); result // => [10, 20, 30]
In Clojure
(def xs [1 2 3]) (def result (map (fn [x] (* 10 x)) xs))
Functional
Programming with values instead of places
Immutable by default
Values cannot be altered!
var things = ["frizzle", "scrooble", "yow"]; things[1] = "whoops";
things // => ["frizzle", "whoops", "yow"]
Immutable by default
var things = ["frizzle", "scrooble", "yow"];
frob(things);
things // => ?
Immutable by default
var init = 0; var counter = init; frob(init); counter++;
init // => 0
Dynamically typed
Opinionated
Clojure values simplicity
Simple versus complex
Easy versus hard
Simple versus complex
objective
Simple versus complex
Easy versus hard
relative / subjective
LISP
“The greatest single programming language ever designed.” – Alan Kay
“Lisp is a language for doing what you've been told is impossible.” – Kent Pitman
LISP
A program from ca. 1961:
DEFINE (( (MEMBER (LAMBDA (A X) (COND ((NULL X) F) ((EQ A (CAR X)) T) (T (MEMBER A (CDR X))) ))) (UNION (LAMBDA (X Y) (COND ((NULL X) Y) ((MEMBER (CAR X) Y) (UNION (CDR X) Y)) (T (CONS (CAR X) (UNION (CDR X) Y))) ))) (INTERSECTION (LAMBDA (X Y) (COND ((NULL X) NIL) ((MEMBER (CAR X) Y) (CONS (CAR X) (INTERSECTION (CDR X) Y))) (T (INTERSECTION (CDR X) Y)) ))) )) INTERSECTION ((A1 A2 A3) (A1 A3 A5)) UNION ((X Y Z) (U V W X))
LISP
Lots of
Irritating
Superfluous
Parentheses
LISP
Anyone could learn Lisp in one day, except that if they already knew Fortran, it would take three days.
– Marvin Minsky
Syntax: Literals
Numbers
10 5/3 10.4
Strings
"This is a string" "This is a multi-line string"
Keywords
:some-keyword :and-a/namespaced-keyword
Symbols
a-symbol
a-namespaced/symbol
Syntax: Literals
Vectors
["one" :two 3]
Lists
(1 2 3 4 5)
(frob "zork" (twiddle 10 18))
Maps
{:foo 1 :bar 2}
Sets
#{1 2 3}
Function application
frob_numbers(1, 2, 3);
… becomes …
(frob-numbers 1 2 3)
- move opening parenthesis to the left
- remove line-noise
Arithmetic
It's just function application!
10 + 9 + 4 * (5 - 3)
… becomes …
(+ 10 9 (* 4 (- 5 3)))
No special precedence rules!
Interactive Development
REPL = Read-Eval-Print-Loop
Explore your code interactively
Interactive Development
Custom functions
A function that squares a number
(fn [x] (* x x))
Applying it
((fn [x] (* x x)) 10) ; => 100
Naming Things
(def square (fn [x] (* x x)))
(square 10) ; => 100
(defn square [x] (* x x))
(def almost-pi (/ 22 7))
Macros
Lisp is a programmable programming language.
– John Foderaro
Macros
Homoiconicity
Code is Data
Data is Code
Primacy of Data
It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.
– Alan J. Perlis
Concurrency
What's the problem with concurrency?
Race conditions!
var xs = [1, 2, 3]; Thread A | Thread B ------------------------------------------+------------------ | print("summing " + xs); | var sum = 0; | | for (var i = 0; i < xs.length; i++) { | sum += xs[i]; | xs[2] = 5; } | | print(sum); // might print 6 or 8
Possible remedies
Defensive copying
var xs = [1, 2, 3]; var ys = xs.slice(); xs[2] = 5; xs // => [1, 2, 5]; ys // => [1, 2, 3];
Drawbacks
- Lots of copying
- Easy to forget
Possible remedies
Locking
synchronized(xs, function() { // sum xs });
Drawbacks
- Blocks both readers and writers
- May easily be too coarse grained
- Possibility of dead locking
- Leaks into user code
Clojure's concurrency model
Managed references
Immutable values
Example
(def xs (atom [1 2 3])) Thread A | Thread B -------------------------------+----------------------- (let [cxs (deref xs)] | (println "summing" cxs) | (swap! xs assoc 2 5) (println (reduce + cxs))) | | ;; this will always print 6
For the JVM
Write once, run away
For the JVM
Tight interop with Java
Build on existing library ecosystem
Profit from VM optimizations
Run (almost) anywhere
Slow startup times
Memory hungry by default
Interop
Call methods
Instantiate objects
Reify or implement interfaces
Clojure types implement common interfaces
e.g. Clojure functions are java.lang.Runnable
Live demo
Ring
A modular HTTP server abstraction, similar to Rack or WSGI
A ring application is a regular Clojure function
It takes a request map as its argument
{:request-method :get :server-host "localhost" :server-port 8080 :uri "/hello"}
And it returns a response map
{:status 200 :body "Hello, world!"}
Tools
Leiningen
Cursive for IntelliJ
Counterclockwise for Eclipse
Cider for Emacs
VimClojure
Light Table
…
Literature
Fogus / Houser: The Joy Of Clojure. Thinking the Clojure Way.
Halloway / Bedra: Programming Clojure.
Sierra / VanderHart: ClojureScript. Up and Running.
Interactive tutorials
Useful URLs
Talks
The end
Thank you!
/