the curious clojurist - neal ford (thoughtworks)
DESCRIPTION
Presented at JAX London 2013 Clojure is the most interesting new language on the horizon, but many developers suffer from the Blub Paradox when they see the Lisp syntax. This talk introduces Clojure to developers who haven’t been exposed to it yet, focusing on the things that truly set it apart from other languages.TRANSCRIPT
The Curious Clojure-ist
1
Agenda Data
Data as Code
Destructuring
Macros
Protocols
The Expression Problem
Concurrency
2
Data
3
datahttps://github.com/edn-format/edn
Extensible Data Notationednedn
4
edn Person5
edn ⊇ Clojure syntax
used by Datomic and others as data transfer format
language/implementation neutral
edn is a system for the conveyance of values.
characteristicsedn
edn is a system for the conveyance of values.
6
a type system
schema based
a system for representing objects
edn is a system for the conveyance of values.
NOT:
7
Scalars
nil nil, null, or nothing
booleans true or false
stringsenclosed in “double quotes”
may span multiple lines\t \r \n supported
characters\c
\newline, \return, \space and \tab
8
Scalars
integers0-9
negative
floating point 64-bit (double) precision is expected.
9
Names
symbols
used to represent identifiersshould map to something other than strings
may include namespace prefixs:my-namespace/foo
keywords
identifiers that designate themselvessemantically akin to enumeration values
symbols that must start with ::fred or :my/fred
10
Collections
listsa sequence of values
zero or more elements within ()(a b 42)
vectors
a sequence of values……that supports random access
zero or more elements within [][a b 42]
11
Collections
maps
collection of key/value associationsevery key should appear only once
unorderedzero or more elements within {}
{:a 1, "foo" :bar, [1 2 3] four}
sets
collection of unique valuesunordered
heterogeneouszero or more elements within #{}
#{a b [1 2 3]}
12
Data as Code
13
Clojure Syntax
edn + …
14
Functions
fn callarg
semantics:
structure:stringsymbol
list
15
Operators (No Different than Functions)
fn call args
list
16
Defining Functions17
defn Semanticsdefine a
fn fn namedocstring
arguments
fn body
18
defn Structure
symbol symbolstring
vector
list
19
Multi-arity
functionmeta-data
20
Control Flow
21
Decisions
true branch
false branch
22
Decisions
23
Refactor
More Arities
24
Don’t Forget…
25
(source …)
26
Namespaces
27
Namespace Declaration
(ns com.example.foo)
names correspond to Java packages,imply same directory structure
28
Namespace Declaration
(ns com.example.foo (:require clojure.data.generators clojure.test.generative))
load some libs
29
Namespace Declaration
(ns com.example.foo (:require [clojure.data.generators :as gen] [clojure.test.generative :as test]))
provide short aliasesfor other libs
30
Namespace Declaration
(ns ^{:author "Stuart Halloway" :doc "Data generators for Clojure."} clojure.data.generators (:refer-clojure :exclude [byte char long ...]) (:require [clojure.core :as core]))
namespace metadata
31
Don’t Do This
(ns com.example.foo (:use clojure.test.generative))
“:use” makes all names unqualified
32
Seqs
33
Sequences Abstraction of traditional Lisp lists
(seq coll)
if collection is non-empty, return seq object on it, else nil
(first seq)
returns the first element
(rest seq)
returns a sequence of the rest of the elements
34
Laziness Most of the core library functions that produce
sequences do so lazily
e.g. map, filter etc
And thus if they consume sequences, do so lazily as well
Avoids creating full intermediate results
Create only as much as you consume
Work with infinite sequences, datasets larger than memory
35
Sequences(drop 2 [1 2 3 4 5]) -> (3 4 5)
(take 9 (cycle [1 2 3 4]))-> (1 2 3 4 1 2 3 4 1)
(interleave [:a :b :c :d :e] [1 2 3 4 5])-> (:a 1 :b 2 :c 3 :d 4 :e 5)
(partition 3 [1 2 3 4 5 6 7 8 9])-> ((1 2 3) (4 5 6) (7 8 9))
(map vector [:a :b :c :d :e] [1 2 3 4 5])-> ([:a 1] [:b 2] [:c 3] [:d 4] [:e 5])
(apply str (interpose \, "asdf"))-> "a,s,d,f"
(reduce + (range 100)) -> 495036
Seq Cheat Sheet
clojure.org/cheatsheet
37
Vectors
38
(def v [42 :rabbit [1 2 3]])
(v 1) -> :rabbit
(peek v) -> [1 2 3]
(pop v) -> [42 :rabbit]
(subvec v 1) -> [:rabbit [1 2 3]]
(contains? v 0) -> true ; subtle
(contains? v 42) -> false ; subtle
Vectors
39
Maps
40
(def m {:a 1 :b 2 :c 3})
(m :b) -> 2 ;also (:b m)
(keys m) -> (:a :b :c)
(assoc m :d 4 :c 42) -> {:d 4, :a 1, :b 2, :c 42}
(dissoc m :d) -> {:a 1, :b 2, :c 3}
(merge-with + m {:a 2 :b 3}) -> {:a 3, :b 5, :c 3}
Maps
41
Nested Structures(def jdoe {:name "John Doe", :address {:zip 27705, ...}})
(get-in jdoe [:address :zip])-> 27705
(assoc-in jdoe [:address :zip] 27514)-> {:name "John Doe", :address {:zip 27514}}
(update-in jdoe [:address :zip] inc) -> {:name "John Doe", :address {:zip 27706}}
42
Sets(use clojure.set)(def colors #{"red" "green" "blue"})(def moods #{"happy" "blue"})
(disj colors "red")-> #{"green" "blue"}
(difference colors moods)-> #{"green" "red"}
(intersection colors moods)-> #{"blue"}
(union colors moods)-> #{"happy" "green" "red" "blue"}
bonus: all relational algebra primitives
supported forsets-of-maps
43
Destructuring
44
Pervasive Destructuring
DSL for binding names
Works with abstract structure
Available wherever names are made*
Vector binding forms destructure sequential things
Map binding forms destructure associative things
45
Why Destructure?
(defn next-fib-pair [pair] [(second pair) (+ (first pair) (second pair))])
(iterate next-fib-pair [0 1])-> ([0 1] [1 1] [1 2] [2 3] [3 5] [5 8] [8 13]...)
without destructuring, next-fib-pair is dominated by code to “pick apart” pair
destructure it yourself…
46
Sequential Destructure
(defn next-fib-pair [[a b]] [b (+ a b)])
(iterate next-fib-pair [0 1])-> ([0 1] [1 1] [1 2] [2 3] [3 5] [5 8] [8 13] ...)
…or you can do the samething with a simple []
47
Simple Things Inline
(defn fibs [] (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
which makes next-fib-pairso simple that you willprobably inline it away!
48
Associative Data
(defn format-name [person] (str/join " " [(:salutation person) (:first-name person) (:last-name person)]))
(format-name {:salutation "Mr." :first-name "John" :last-name "Doe"})-> "Mr. John Doe"
same problem as before:code dominated bypicking apart person
49
Associative Destructure
(defn format-name [name] (let [{salutation :salutation first-name :first-name last-name :last-name} name] (str/join " " [salutation first-name last-name]))
(format-name {:salutation "Mr." :first-name "John" :last-name "Doe"})-> "Mr. John Doe"
pick apart name
50
The :keys Option
(defn format-name [{:keys [salutation first-name last-name]}] (str/join " " [salutation first-name last-name]))
(format-name {:salutation "Mr." :first-name "John" :last-name "Doe"})-> "Mr. John Doe"
a common scenario:parameter names and key names are the same, so say
them only once
51
Optional Keyword Args
(defn game [planet & {:keys [human-players computer-players]}] (println "Total players: " (+ human-players computer-players))) (game "Mars” :human-players 1 :computer-players 2)Total players: 3
not a language feature, simply a consequence of variable arity fns
plus map destructuring
52
Platform Interop
53
Java new
java new Widget("foo")
clojure sugar (Widget. "red")
54
Access Static Members
java Math.PI
clojure sugar Math/PI
55
Access Instance Members
java rnd.nextInt()
clojure sugar (.nextInt rnd)
56
Chaining Access
java person.getAddress().getZipCode()
clojure sugar (.. person getAddress getZipCode)
57
Parenthesis Count
java ()()()()
clojure ()()()
58
all forms are created equal !
interpretation is everything
59
form syntax example
function list (println "hello")operator list (+ 1 2)
method call list (.trim " hello ")import list (require 'mylib)
metadata list (with-meta obj m)control flow list (when valid? (proceed))
scope list (dosync (alter ...))
all forms are created equal !
60
Special Forms
61
Special Forms (def symbol init?)
(if test then else?)
(do exprs*)
(quote form)
(fn name? [params*] exprs*)
(fn name? ([params*] exprs*)+)
(let [bindings*] exprs*)
(loop [bindings*] exprs*)
(recur exprs*)
(throw expr)
(try expr* catch-clause* finally-clause?)
62
Macros
63
Programs writing Programs
Reader
evaluator/compiler
Effect
data structures
Code
Text
bytecode
You
JVM
characters
characters
Program
data structures
Reader
evaluator/compiler
Effect
data structures
Code
Text
bytecode
You
JVM
characters
characters
Program
data structures
Program(macro)
data structures
64
Inside Out?
{:name "Jonathan"}
(assoc {:name "Jonathan"} :nickname "Jon")
(dissoc (assoc {:name "Jonathan" :password "secret"} :nickname "Jon") :password)
65
Thread First ->
(-> {:name "Jonathan" :password "secret"} (assoc :nickname "Jon") (dissoc :password))
(dissoc (assoc {:name "Jonathan" :password "secret"} :nickname "Jon") :password)
66
Syntactic Abstraction
Reader
evaluator/compiler
Effect
data structures
Code
Text
bytecode
You
JVM
characters
characters
Program
data structures
Program(macro)
data structures
67
Seq Ops Inside Out
(range 10)
(map inc (range 10))
(filter odd? (map inc (range 10)))
(reduce + (filter odd? (map inc (range 10))))
68
Thread Last ->>
(->> (range 10) (map inc) (filter odd?) (reduce +))
(reduce + (filter odd? (map inc (range 10))))
69
defrecord
70
AFn
IFn
ifn?
AFunction
APersistentVector
APersistentMap
APersistentSet
Keyword
MultiFn
Ref
RestFn
Symbol
Var Callability
Fn
fn?
71
From Maps...(def stu {:fname "Stu" :lname "Halloway" :address {:street "200 N Mangum" :city "Durham" :state "NC" :zip 27701}})
(:lname stu)=> "Halloway"
keyword access
(-> stu :address :city)=> "Durham"
nested access
(assoc stu :fname "Stuart")=> {:fname "Stuart", :lname "Halloway", :address ...}
update
(update-in stu [:address :zip] inc)=> {:address {:street "200 N Mangum", :zip 27702 ...} ...}
nestedupdate
data oriented
72
...to Records!(defrecord Person [fname lname address])(defrecord Address [street city state zip])(def stu (Person. "Stu" "Halloway" (Address. "200 N Mangum" "Durham" "NC" 27701)))
(:lname stu)=> "Halloway"
(-> stu :address :city)=> "Durham"
(assoc stu :fname "Stuart")=> :user.Person{:fname "Stuart", :lname"Halloway", :address ...}
(update-in stu [:address :zip] inc)=> :user.Person{:address {:street "200 N Mangum", :zip 27702 ...} ...}
still data-oriented:everything works
as beforetype is therewhen you
care
object oriented
73
defrecord(defrecord Foo [a b c])-> user.Foo
named typewith slots
(def f (Foo. 1 2 3))-> #'user/f positional
constructor(:b f)-> 2 keyword
access(class f)-> user.Foo plain ol'
class(supers (class f))-> #{clojure.lang.IObj clojure.lang.IKeywordLookup java.util.Map clojure.lang.IPersistentMap clojure.lang.IMeta java.lang.Object java.lang.Iterable clojure.lang.ILookup clojure.lang.Seqable clojure.lang.Counted clojure.lang.IPersistentCollection clojure.lang.Associative}
casydht*
*Clojure abstracts so you don't have to
74
Protocols
75
(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
Protocols
76
Extending Protocols
77
Extend Protocols Inline
(defrecord Bar [a b c] AProtocol (bar [this b] "Bar bar") (baz [this] (str "Bar baz " c)))
(def b (Bar. 5 6 7))
(baz b)
=> "Bar baz 7"
78
Extend Protocols Inlinefrom ClojureScript
browser.clj
79
Extending to a Type(baz "a")
java.lang.IllegalArgumentException: No implementation of method: :baz of protocol: #'user/AProtocol found for class: java.lang.String
(extend-type String AProtocol (bar [s s2] (str s s2)) (baz [s] (str "baz " s)))
(baz "a")
=> "baz a"
80
Extending to Many Types
from Clojurereducers.clj
note extendto nil
81
Extending to Many Protocols
from ClojureScriptcore.cljs
82
Composition with Extend
from Clojure java/io.clj
the “DSL” for advanced reuse is maps and assoc
83
Reify
(let [x 42 r (reify AProtocol (bar [this b] "reify bar") (baz [this ] (str "reify baz " x)))] (baz r))
=> "reify baz 42"
instantiate an unnamed type implement 0
or more protocols
or interfaces
closes overenvironment
like fn
84
Code Structurepackage com.acme.employees;
Employee
raise()
roles()
updatePersonalInfo()
Manager
roles()
approvalProfile()
interface Employee {}
(namespace com.acme.employees)
(raise )
(updatePersonalInfo )
(roles )
(approvalProfile )
(defprotocol Employee )
85
The Expression Problem
86
The Expression Problem
abstraction
concretion
A B
A should be able to work with B's abstractions, and vice versa,
without modification of the original code
87
Is This Really a Problem?
abstraction
concretion
just use interfaces for abstraction (??)
BA
88
Example: ArrayList vs.the Abstractions
java.util.List
ArrayList
clojure.lang.Counted
clojure.lang.Seqable
?89
Example: String vs.the Abstractions
java.util.List
String clojure.lang.Counted
clojure.lang.Seqable
?
90
A Can't Inherit from B
B is newer than A
A is hard to change
We don’t control A
happens even within a single library!
91
Some Approachesto the Expression
Problem
92
1. Roll-your-own
if/then instanceof? logic
closed
93
A Closed World
94
so make a NiftyString
that is
2. Wrappers
NiftyString
java.util.List
String
java.util.Collectionstrings are
not collections
95
Wrappers = Complexity
Ruin identity
Ruin Equality
Cause nonlocal defects
Don’t compose: AB + AC ≠ ABC
Have bad names
96
3. Monkey Patching
common in e.g. rubynot possible in java
String
java.util.List
java.util.Collectionsneak in
and change them!
strings are not
collections
97
Monkey Patching = Complexity
Preserves identity (mostly)
Ruins namespacing
Causes nonlocal defects
Forbidden in some languages
98
4. Generic Functions (CLOS)
don't touch existing implementation,
just use it
String
map
reduce
count
polymorphism lives in the
fns
99
Generic Functions
Decouple polymorphism & types
Polymorphism in the fns, not the types
no “isa” requirement
no type intrusion necessary
100
protocols = generic functions - arbitrary dispatch + speed + grouping
(and still powerful enough tosolve the expression problem!)
101
Concurrency
102
concurrency, coincidence of events or space
parallelism, the execution of operations concurrently by separate parts of a computer
103
Our Tools
threads
104
Our Tools
42
places
105
critical sections
Our Tools
42
42 42
106
memory, the capacity ... for returning to a previous state when the cause of the transition from that state is removed
record, the fact or condition of having been written down as evidence...... an authentic or official report
107
Memory, Records = Places?
Memory is small and expensive
Storage is small and expensive
Machines are precious, dedicated resources
Applications are control centers
108
A Different Approach
New memories use new places
New records use new places
New moments use new places
“In-place” changes encapsulated by constructors
109
Values
110
Values
Immutable
Maybe lazy
Cacheable (forever!)
Can be arbitrarily large
Share structure
111
What Can Be a Value?
42
112
What Can Be a Value?
{:first-name "Stu", :last-name "Halloway"}
42
113
What Can Be a Value?
42{:first-name "Stu", :last-name "Halloway"}
114
What Can Be a Value?
42{:first-name "Stu", :last-name "Halloway"}
115
What Can Be a Value?
42{:first-name "Stu", :last-name "Halloway"}
Anything?
116
References
Refer to values (or other references)
Permit atomic, functional succession
Model time and identity
Compatible with a wide variety of update semantics
117
Epochal Time Model
v1 v2 v3
values
118
Epochal Time Model
v1 v2 v3
f1 f2
functions
119
Epochal Time Model
v1 v2 v3
f1 f2
atomic succession
120
Epochal Time Model
v1 v2 v3
f1 f2
reference
121
Epochal Time Model
v1 v2 v3
f1 f2
observers perceive identity, can remember and record
122
Epochal Time Model
v1 v2 v3
f1 f2
observers do notcoordinate
123
Epochal Time Model
v1 v2 v3
f1 f2
124
Atoms
125
Atoms
(def a (atom 0))
(swap! a inc)=> 1
(compare-and-set! a 0 42)=> false
(compare-and-set! a 1 7)=> true
126
Atoms
(def a (atom 0))
(swap! a inc)=> 1
(compare-and-set! a 0 42)=> false
(compare-and-set! a 1 7)=> true
functional succession
127
Atoms
(def a (atom 0))
(swap! a inc)=> 1
(compare-and-set! a 0 42)=> false
(compare-and-set! a 1 7)=> true
optimistic concurrency
128
Software Transactional Memory
129
Software Transactional Memory
Refs can change only within a transaction
Provides the ACI in ACID
Transactions are speculative, will be retried
130
F
v1 v2 v3 v4
v1 v2 v3 v4
v1 v2 v3 v4
v1 v2 v3 v4
F
F
F
F
F
F
F F F
F
F
Transactions131
Transactions
(defn transfer [from to amount] (dosync (alter from - amount) (alter to + amount)))
(alter from - 1)=> IllegalStateException No transaction running
132
(defn transfer [from to amount] (dosync (alter from - amount) (alter to + amount)))
(alter from - 1)=> IllegalStateException No transaction running
Transactions
scope transaction
133
(defn transfer [from to amount] (dosync (alter from - amount) (alter to + amount)))
(alter from - 1)=> IllegalStateException No transaction running
Transactions
functional succession
134
Transactions
(defn transfer [from to amount] (dosync (alter from - amount) (alter to + amount)))
(alter from - 1)=> IllegalStateException No transaction running
coordination guaranteed!
135
STM Details
Uses locks, latches internally to avoid churn
Deadlock detection and barging
No read tracking
Readers never impede writers
Nobody impedes readers
136
Summary
137
Summary
Serious Lisp on the JVM
Built as a destination
Advanced language features
Advanced implementation
Secret weapon?
138
Clojure in the wild?“We re-coded our flagship application XXXXXX from Java to Clojure about a year ago. NEVER looked back. Why?
Reduced our lines of code down by at least half.
Support and bugs have likewise been cut by about 65-70%.
We have large enterprise clients. How did we get Clojure into these very old guard environments????
139
Between you and me....
We don't talk about Clojure. We talk about "Java Extensions" or "the Clojure Java Extension". No one is the wiser.Clients LOVE us for our blistering fast turn around. We present ourselves as a larger company with fake Linkedin employees. We actually only have 4 real employees. But with Clojure we do the same work as if we had 20.”
we lie.
140
?’sThe preceding work is licensed under the Creative Commons Attribution-Share Alike 3.0 License.
http://creativecommons.org/licenses/by-sa/3.0/us/
Presentation PatternsNeal Ford, Matthew McCullough, Nathaniel Schuttahttp://presentationpatterns.com
Functional Thinking bit.ly/nf_ftvideo
Clojure (inside out)Neal Ford, Stuart Hallowaybit.ly/clojureinsideout
141