The
Introduction to Clojure page on the OSCON site.
Neal Ford (ThoughtWorks)
9:00am Tuesday, 07/23/2013
bit.ly/clojureinsideout
My notes from the Introduction to Clojure session.
Clojure is Functional, Dynamic, Hosted, Open Source, has an Atomic Succession Model, Lisp
The most enjoyable part of this presentation was that I was back in Lisp land again. Although I only spent 6 to 12 months working in Lisp I love the radically different structure and syntax of this language.
Data:
EDN Extensible Data Notation
data is immutable
nil - nil, null, nothing
booleans - true or false
strings - double quotes, can span multiple lines,, include \t \r \n
char - same as JVM
integer - same
double - same
Two syntaxes for naming things
symbols - e.g. variable names and namespaces, should map to something
keywords - like enumeration values, must start with :
Collections
lists - a sequence of values, zero or more elements within () like lisp
vectors - in [] like an array, supports random access
maps - key/value associations, looks like JSON, can have a vector as key, keys are unique
sets - {} collection of unique values
Clojure syntax is edn + language syntax
println("Hello World") becomes (println "Hello, world")
Operators: (+ 1 2 3 4 5)
Infix
int x = 40 - (5 + 10 * 2) becomes:
(def x (- 40 (* 2 (+ 5 10))))
Defining functions
(defn greet <- name
"some text" <-
[your-name]
(str "Hello, " your-name))
(defn larger [x y]
(if (> x y) x y ))
Anonymous Functions
(map (fn [x] (* x 2)) (range 10))
i.e. pass functions as parameters
result (0 2 4 ... 18)
syntactic sugar for above:
(map #(* % 2) (range 10))
Namespaces
(ns com.example.foo)
- maps to JVM as you'd expect
Can wrap requires:
(:require...
Can add namespace meta data
Use :exclude to override (e.g. override + for vector math)
Don't use :use
Platform Interop
java Math.PI
Clojure sugar: Math/PI
java dot notation
person.getAddress().getZipCode() becomes:
(.. person getAddress getZipCode)
Clojure is a homoiconic language. i.e. it's a language that's represented by its data structure. Languages that you might have heard of that exhibit homoiconicity:
Curl
Julia
Lisp
Scheme (Lisp dialect)
Clojure (Lisp dialect)
Racket (Lisp dialect)
Mathematica
Prolog
SNOBOL
Tcl
XSLT
Functional Programming
- Easier to reason about - higher level of abstraction
- Easier to test
- Easier to compose
- Essential at scale
Persistent Data Structures
Composite values - immutable
'Change' is function of value to value
Collection maintains performance guarantees
uses Bit-partitioned hash
tries
Constructing Values
Looping via recursion (recur)
prefer higher-order library fns when appropriate
Recursive Loops
No mutable locals in Clojure
No tail recursion optimization in the JVM
recur op does constant-space recursive loping
Rebinds and jumps to nearest loop or function frame
Loop alternatives
(loop
; reduce with adder fn
(reduce
;apply data constructor fn
(apply
;map into empty (or not) structure
(into
;get lucky
(zipmap
Benefits of Abstraction
Better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures - Alan J. Perlis
All Clojure collections have (seq available.
Sequences
Abstraction of traditional Lisp lists
(seq coll)
(first seq)
(rest seq)
In Lisp the first and rest functions used to be called car and cdr.
Lazy Seqs
Evaluated at execution
Very similar to LINQ in C#, same type of syntax
Operations on sequences:
(drop
(take 9 (cycle
(interleave
; splits collection into param partitions
(partition 3
; create key values
(map vector
; use like String.Join():
(apply str (interpose \, "asdf"))
-> "a,s,d,f"
(reduce + (range 100)) -> 4950
(for
is a macro and not an imperative loop
Seq Cheat Sheet
http://clojure.org/cheatsheet
Vectors
(def v [42 :rabbit [1 2 3]])
(v 1) -> :rabbit
(peek v) -> [1 2 3]
(pop v)
(subvec v 1)
(contains? v 0) -> true
(contains? v 42) -> false ; last param is index
Maps
(def m {:a 1 :b 2 :c 3})
(m :b) -> 2 ;also (:b m) will work
(keys m) -> (:a :b :c)
(assoc m :d 4 :c 42) ->
(dissoc m :d)
(merge-with
Nested Structures
(def jdoe {:name "John Does", :address {:zip 27705, ...}})
(get-in
(assoc-in
(update-in
Sets
(use clojure.set)
(def colors #["red" "green" "blue})
(disj
(difference
(intersection
(union
Addition
(def v [1 2 3]) ;vector
(def l '(1 2 3)) ;list
(conj v 42) -> [1 2 3 42]
(conj l 42 -> '(42 1 2 3)
(into v [99 :bottles])
(into l [99 :bottles])
Destruction
Pervasive Destructuring
DSL for binding names
Works with abstract structure
Available wherever names are made
sequential (vector) map (associative)
Special Forms
(def symbol init?)
(if test then else?)
;do returns the last value executed:
(do exprs*)
(quote form
(fn name? [params*] exprs*)
(let [binding
(loop
(recur
(throw
(try expr* catch-clause* finally-clause?]
Macros
Thread First ->
(->
Do everything from inside out - i.e. first statement is inside statement
Thread Last ->>
(->>
Boids
github.com/relevance/boids
Algorithm in Clojure
Cross compiled to ClojureScript
Example: perfect #
Break exercises
clojure.org - install clojure
leiningen.org - build tool
github.com/functional-koans/clojure-koans -> for beginners
projecteuler.net -> if you know the syntax of clojure
Second part of session
Leiningen
run a REPL etc.
"Maven meets Ant (withou the pain)"
RubyGems/Bundler/Rake
Compojure
Lightweight web framework - most popular
MVC based
github.com/abedra/shouter - example compojure app
Traditional OO
objects are mutable
encapsulate change & info
polymorphism lives inside object
extend via inheritance
interfaces are optional
Clojure
expose immutable data
encapsulate change (not data types)
polymorphism a la carte
interfaces are mandatory
extend by composition
defrecord
(defrecord Person [fname lname address])
(defrecord Address...
(def stu (Person. "Stu" "Halloway" (Address...
defrecord Details:
Type fields can be primitives
Value-based equality & hash
in-line methods
defs can inline
keyword field looks can inline
protocols make interfaces
Protocols
(defprotocol AProtocol
"A doc string for AProtocol abstraction"
(bar [a b] "bar docs")
(baz [a] "baz docs"))
named set of generic functions
polymorphic on type of first argument
no implementation
define fns in the same namespaces as protocols
Extending Protocols
(extend-type
Like extension methods in .Net but more extensible
(extend-protocol
ClojureScript extension
; extends JS array
(extend-type array ISequable
Reify
(let [x 42 r (reify AProtocol ; implement 0 or more protocols or interfaces
deftype
use defrecord for information
use deftype
Concurrency - done at the same time
Parallelism - the execution of items in parallel
Values
immutable
maybe lazy
cacheable forever
can be arbitrarily large
share structure
What can be a value?
42
{:first-name "Stu"...}
anything...
References
refer to values or other references
permit atomic, functional succession
model time and identity
compatible with a wide variety of update parameters
API
(def counter (atom 0))
(swap! counter + 10)
Shared Abilities
@some-ref
(deref some-ref)
(add-watch...
Atoms
(def a (atom 0))
(swap! a inc)
=> 1
(compare-and-set! a 0 42)
=> false
(compare-and-set! a 1 7)
=> true
! - mutates
? - returns true or false
Software Transactional Memory (STM)
refs can change only within a transaction
provides the ACI in ACID (Atomic Consistent Isolated)
(defn transfer
[from to amount]
(dosync
(alter from - amount)
(alter to + amount)))
(alter from - 1)
=> IllegalStateException No transaction running
STM details
uses locks, latches internally to avoid churn
deadlock detection and barging
no read tracking
only Haskell and Clojure have transactional memory
readers never impede writers
nobody impedes readers
Pending references
work not done yet
Future
(def result (future...
(defef result 1000 or-else) ; timeout
@result deref result) ; wait without timeout
Delay
(def result (delay dont-need-yet))
Promise
(def result (promise)) ; no-org constructor
Java API
all work in Clojure
thin wrapper over Java API
Summary
Clojure is a 21st century Lisp
popular with framework designers
most advanced language constructs
most interoperable with underlying platform
powerful abstractions
active community
"a programming language beamed back from the near future" -Stuart Halloway