Clojure: Functional Concurrency for the JVM Howard M. Lewis Ship Director of Open Source Technology Formos Software Development howard.lewis.ship@formos.com © 2009 Formos Software Development
Clojure: The Language © 2009 Formos Software Development
http://xkcd.com/297/ Clojure: The Language © 2009 Formos Software Development
Rich Hickey © 2009 Formos Software Development
Code is Data Quoted list of '(1 2 3) numbers (biggest 5 42) Function call Function definition (defn biggest "Find the maximum of two numbers" [x y] (if (> x y) x y)) © 2009 Formos Software Development
Read Eval Print Loop user=> (defn biggest "Find the maximum of two numbers" [x y] (if (> x y) x y)) #=(var user/biggest) user=> (biggest 5 42) 42 user=> (doc biggest) ------------------------- user/biggest ([x y]) Find the maximum of two numbers nil user=> '(1 2 3) (1 2 3) user=> '(biggest 5 42) (biggest 5 42) user=> (first '(biggest 5 42)) biggest user=> (eval '(biggest 5 42)) 42 © 2009 Formos Software Development
There Is No Interpreter Source Code Repl Input Clojure User Classes Java Evaluator Compiler Clojure Source Files Java Libraries JVM Operating System © 2009 Formos Software Development
Clojure Literals user=> 42 42 user=> "A Clojure String" "A Clojure String" user=> nil nil user=> :balance :balance user=> true true user=> false false © 2009 Formos Software Development
Clojure Literals user=> 5 5 user=> 5.001 5.001 user=> 22/7 22/7 user=> (* 2 22/7) 44/7 user=> (* 100000 100000 100000) 1000000000000000 user=> (+ 5. 0.000000000000000001) 5.0 user=> (+ 5.0M 0.000000000000000001M) 5.000000000000000001M © 2009 Formos Software Development
Java Interop factory.setNamespaceAware(true) (.setNamespaceAware factory true) new StringBuffer() (new StringBuffer) (StringBuffer.) factory.newSAXParser().parse(src, handler) (.. factory newSAXParser (parse src handler)) MyObject.ivar = "foo"; (set! (. MyObject ivar) "foo") © 2009 Formos Software Development
Java Interop frame.add(panel, BorderLayout.CENTER); frame.add(greetButton, BorderLayout.SOUTH); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); (doto frame (.add panel BorderLayout/CENTER) (.add greet-button BorderLayout/SOUTH) (.pack) (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) (.setVisible true)) © 2009 Formos Software Development
Clojure Collections: Lists lst user=> (def lst `(1 2 3)) #=(var user/lst) user=> lst (1 2 3) user=> (first lst) 1 1 user=> (rest lst) (2 3) user=> (conj lst 4) 2 (4 1 2 3) user=> (cons 4 lst) (4 1 2 3) 3 © 2009 Formos Software Development
Clojure Collections: Lists 4 lst user=> (def lst `(1 2 3)) #=(var user/lst) user=> lst (1 2 3) user=> (first lst) 1 1 user=> (rest lst) (2 3) user=> (conj lst 4) 2 (4 1 2 3) user=> (cons 4 lst) (4 1 2 3) 3 © 2009 Formos Software Development
Clojure Collections: Vectors user=> (def v [:moe :larry :curly]) #=(var user/v) user=> v [:moe :larry :curly] user=> (first v) :moe user=> (rest v) (:larry :curly) user=> (conj v :shemp) [:moe :larry :curly :shemp] user=> (cons :shemp v) (:shemp :moe :larry :curly) user=> v [:moe :larry :curly] user=> (v 1) :larry vector is a function of its indexes © 2009 Formos Software Development
Clojure Collections: Map user=> (def m {:first-name "Howard" :last-name "Lewis Ship"}) #=(var user/m) user=> m {:last-name "Lewis Ship", :first-name "Howard"} user=> (get m :last-name) "Lewis Ship" map is a user=> (m :last-name) function of "Lewis Ship" its keys user=> (assoc m :company "Formos") {:company "Formos", :last-name "Lewis Ship", :first-name "Howard"} user=> m {:last-name "Lewis Ship", :first-name "Howard"} user=> (:first-name m) "Howard" user=> (:ssn m) nil Keywords are functions, too! © 2009 Formos Software Development
Clojure Collections: Sets user=> (def s #{"Howard" "Suzanne" "Molly" "Jim"}) #=(var user/s) user=> s #{"Howard" "Jim" "Molly" "Suzanne"} user=> (contains? s "Howard") true user=> (contains? s "howard") false set is a user=> (s "Howard") function of "Howard" its elements user=> (s "Rhys") nil user=> (conj s "Howard") #{"Howard" "Jim" "Molly" "Suzanne"} user=> (conj s "Scott") #{"Howard" "Jim" "Molly" "Suzanne" "Scott"} © 2009 Formos Software Development
© 2009 Formos Software Development
❝For alumni of other languages, beginning to use Lisp may be like stepping onto a skating rink for the first time. It’s actually much easier to get around on ice than it is on dry land—if you use skates. Till then you will be left wondering what people see in this sport.❞ Paul Graham © 2009 Formos Software Development
Functional Programming © 2009 Formos Software Development
Functional Programming © 2009 Formos Software Development
© 2009 Formos Software Development
No Mutable State© 2009 Formos Software Development
© 2009 Formos Software Development
No Side Effects © 2009 Formos Software Development
© 2009 Formos Software Development
First Class Functions © 2009 Formos Software Development
© 2009 Formos Software Development
Functional Composition © 2009 Formos Software Development
Functional Programming in Java public void saveOrUpdate(final Employee employee) { HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException,SQLException { session.saveOrUpdate(employee); return null; } }; hibernateTemplate.execute(callback); } Outer function controls the SwingUtilities.invokeLater(new Runnable() context: { public void run() • Thread { • Exception handling progressBar.setValue(progressBar.getValue() + 1); } • Parameters }); © 2009 Formos Software Development
Functional Java Collections public interface Predicate<T> { boolean accept(T value); } public static <T> Collection<T> filter(Predicate<T> pred, Collection<T> coll) { Collection<T> out = new ArrayList<T>(); for (T item : coll) { if (pred.accept(item)) out.add(item); } return out; } return CollectionUtils.filter(new Predicate<String>() { public boolean accept(String value) { return !value.startsWith("."); } }, names); © 2009 Formos Software Development
Functional Clojure Collections Function Anonymous parameter function (filter #(not (.startsWith % ".")) names) Member access form user=> (def names ["fred" "barney" ".hidden" "wilma"]) #=(var user/names) user=> (filter #(not (.startsWith % ".")) names) ("fred" "barney" "wilma") user=> (remove #(.startsWith % ".") names) ("fred" "barney" "wilma") user=> © 2009 Formos Software Development
First Class Functions (filter #(not (.startsWith % ".")) names) function as parameter to function (defn require-extension [ext] (fn [file-name] (= ext (last (split-string file-name "."))))) function as return value (defn filter-by-extension [ext coll] (filter (require-extension ext) coll)) composing functions © 2009 Formos Software Development
Bridging Java and Clojure SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(progressBar.getValue() + 1); } }); Invoke static method (SwingUtilities/invokeLater #(.setValue progressBar (inc (.getValue progressBar)))) Clojure functions implement: • Runnable • Callable • Comparator © 2009 Formos Software Development
Life without the for loop public static int sum(int[] vals) { int total = 0; col for (int val : vals) total += val; return total; } x y (defn sum z [col] (reduce + 0 col)) ➠ 0 + col[0] + col[1] + col[2] ... © 2009 Formos Software Development
Life without the for loop public static int sum(int[] vals) { int total = 0; col for (int val : vals) total += val; return total; } x y (defn sum z [col] (reduce + 0 col)) ➠ 0 + col[0] + col[1] + col[2] ... ➠ (+ 0 (first col)) (+ (+ 0 (first col)) (first (rest col))) (+ (+ (+ 0 (first col)) (first (rest col))) (first (rest (rest col)))) ... © 2009 Formos Software Development
Life without the for loop public static String[] formatDoubles(double[] inputs) { String[] output = new String[inputs.length]; for (int i = 0; i < input.length; i++) output[i] = String.format("%9.2f", inputs[i]); return output; } (defn format-doubles f [col] (map #(format "%9.2f" %) col)) f Apply function to each item, forming f new seq user=> (format-doubles '(2.5 3.7 -22.7)) (" 2.50" " 3.70" " -22.70") © 2009 Formos Software Development
for: list comprehension user=> (range 0 5) (0 1 2 3 4) user=> (for [x (range 0 10) :when (even? x)] x) (0 2 4 6 8) user=> (for [x (range 1 5) y (range 0 x)] [x y]) ([1 0] [2 0] [2 1] [3 0] [3 1] [3 2] [4 0] [4 1] [4 2] [4 3]) © 2009 Formos Software Development
for: list comprehension user=> (range 0 5) (0 1 2 3 4) user=> (for [x (range 0 10) :when (even? x)] x) (0 2 4 6 8) user=> (for [x (range 1 5) y (range 0 x)] [x y]) ([1 0] [2 0] [2 1] [3 0] [3 1] [3 2] [4 0] [4 1] [4 2] [4 3]) (defn convert-attributes-to-tokens [attrs] (for [x (range (.getLength attrs))] (let [uri (.getURI attrs x) name (.getLocalName attrs x) value (.getValue attrs x) token (…) ] token))) © 2009 Formos Software Development
Laziness is a Virtue Image © 2007 Jon Fife http://flickr.com/photos/good-karma/577632972/ © 2009 Formos Software Development
Laziness is a Virtue user=> (take 20 (iterate inc 1)) (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) user=> (take 20 (map * (iterate inc 1) (iterate inc 1))) (1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400) Image © 2007 Jon Fife http://flickr.com/photos/good-karma/577632972/ © 2009 Formos Software Development
Laziness © 2009 Formos Software Development
Java: Data Encapsulated in Objects Person Person Person Person firstName: "Howard" firstName: "Scott" firstName: "Molly" firstName: "David" lastName: "Lewis Ship" lastName: "Simon" lastName: "Newman" lastName: "Goldman" age: 42 age: 44 age: 29 age: 42 public double averageAge(Collection<Person> persons) { double total = 0.0; for (Person p : persons) total += p.getAge(); return total / persons.size(); } © 2009 Formos Software Development
Clojure: Data in Transformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> © 2009 Formos Software Development
Clojure: Data in Transformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> (defn avg-age [coll] (/ (apply + (map :age coll)) (count coll))) © 2009 Formos Software Development
Clojure: Data in Transformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> (defn avg-age [coll] (/ (apply + (map :age coll)) (count coll))) (defn avg (defn avg-age [f coll] [coll] (/ (apply + (map f coll))) (count coll))) (avg :age coll)) © 2009 Formos Software Development
Clojure: Data in Transformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> (defn avg-age [coll] (/ (apply + (map :age coll)) (count coll))) (defn avg (defn avg-age [f coll] [coll] (/ (apply + (map f coll))) (count coll))) (avg :age coll)) (avg #(count (:last-name %)) persons) © 2009 Formos Software Development
© 2009 Formos Software Development
❝Somehow the idea of reusability got attached to object-oriented programming in the 1980s, and no amount of evidence to the contrary seems to be able to shake it free.❞ Paul Graham © 2009 Formos Software Development
Clojure Concurrency © 2009 Formos Software Development
Solving Deadlocks: Timeout & Retry © 2009 Formos Software Development
Solving Deadlocks: Specific Lock Order © 2009 Formos Software Development
Solving Deadlocks: Coarse Locks Image © 2008 Marcin Wichary http://flickr.com/photos/mwichary/2222776430/ © 2009 Formos Software Development
Locks are the Enemy Image © 2007 James Manners http://flickr.com/photos/jmanners/443421045/ © 2009 Formos Software Development
Clojure: Software Transactional Memory (def savings (ref 1000.)) (def checking (ref 2000.)) (def mm (ref 7000.)) (defn transfer "Transaction to transfer money from one account to another." [from to amount] (dosync (alter from - amount) (alter to + amount))) (transfer checking savings 500.) (transfer mm checking 300.) - 500. + 500. - 300. + 300. Checking 1000. 500. 500. Savings 2000. 2500. 2800. 2800. MM 7000. 6700. 6700. © 2009 Formos Software Development
Retries: Transactions Are Speculative (def savings (ref 1000.)) (def checking (ref 2000.)) (def mm (ref 7000.)) (defn transfer "Transaction to transfer money from one account to another." [from to amount] (dosync (alter from - amount) (alter to + amount))) (transfer checking savings 500.) (transfer mm checking 300.) + 300. - 500. - 300. + 500. - 300. + 300. Checking 1000. 500. X 500. Savings 2000. 2500. 2300. 2500. 2800. 2800. MM 7000. 6700. 7000. 6700. 6700. © 2009 Formos Software Development
© 2009 Formos Software Development
No Blocking © 2009 Formos Software Development
© 2009 Formos Software Development
No Locks © 2009 Formos Software Development
© 2009 Formos Software Development
Persistent Collections © 2009 Formos Software Development
Managing Mutation • What can change? • Reference types: atom, var, agent, ref • When can they change? • When are changes visible to other threads? Image © 2008 Daniel Chan http://flickr.com/photos/chanchan222/2847443980/ © 2009 Formos Software Development
Atoms • Shared, Global • Changes are atomic, synchronous, & non-blocking • (swap!): Pass value to function yielding new value • (reset!): Force new value, regardless of existing value user=> (def queue (atom [])) #'user/queue user=> @queue [] user=> (swap! queue conj {:parse "http://www.clojure.org/"}) [{:parse "http://www.clojure.org/"}] user=> @queue [{:parse "http://www.clojure.org/"}] user=> (reset! queue []) [] user=> @queue [] user=> © 2009 Formos Software Development
Vars — Per-Thread Mutables • (def) sets global binding • (binding) to set up a per-thread override Image © 2005 Jack Keene http://www.flickr.com/photos/whatknot/3118124/ • (set!) if per-thread binding user=> (def x 1) #=(var user/x) user=> x 1 user=> (defn manipulate-x [] (binding [x 2] (printf "Local x is %d" x) (set! x 3) (printf "nLocal x is now %dn" x))) #=(var user/manipulate-x) user=> (.run (Thread. manipulate-x)) Local x is 2 Local x is now 3 nil user=> x 1 © 2009 Formos Software Development
Interfacing with Java APIs (def *tokens*) (defn add-token [token] (set! *tokens* (conj *tokens* token))) (def sax-handler (proxy [DefaultHandler] [] (startElement [uri local-name q-name attrs] (flush-text) (add-token …)) … )) (defn tokenize-xml [src] (binding [*tokens* []] (let [factory (SAXParserFactory/newInstance)] (.setNamespaceAware factory true) (.. factory newSAXParser (parse src sax-handler)) *tokens*))) © 2009 Formos Software Development
Everything's a Var! Function Namespace Var Value ... Symbol such as x or map © 2009 Formos Software Development
Functions are stored in Vars user=> (defn say-hello [] (println "Hello")) #'user/say-hello user=> (say-hello) Hello nil user=> (binding [say-hello #(println "Goodbye")] (say-hello)) Goodbye nil user=> (say-hello) Hello nil user=> © 2009 Formos Software Development
Agents — Single Thread Writes user=> (def savings (agent 1000.)) #=(var user/savings) user=> (def checking (agent 2000.)) #=(var user/checking) user=> @savings 1000 user=> @checking 2000 user=> (send savings - 300.) #<clojure.lang.Agent@c3233b> user=> (send checking + 300.) #<clojure.lang.Agent@67e5a7> user=> @savings 700 user=> @checking 2300 •Asynchonous •Single threaded •Non-transactional © 2009 Formos Software Development
Refs — Software Transactional Memory (def savings (ref 1000.)) (def checking (ref 2000.)) (def mm (ref 7000.)) (defn transfer "Transaction to transfer money from one account to another." [from to amount] (dosync (alter from - amount) (alter to + amount))) (transfer checking savings 500.) (transfer mm checking 300.) user=> @checking 2000 user=> @savings 1000 user=> (transfer checking savings 500.) 1500 user=> @checking 1500 user=> @savings 1500 user=> (ref-set savings 2000.) java.lang.IllegalStateException: No transaction running (NO_SOURCE_FILE:0) © 2009 Formos Software Development
Concurrency Notes • All reference types can have a validator function • All refs can have a watcher: an agent notified of changes • (send) inside (dosync) waits until successful completion • No guarantees when calling Java objects © 2009 Formos Software Development
© 2009 Formos Software Development
❝The key to performance is elegance, not battalions of special cases.❞ Jon Bentley and Doug McIlroy © 2009 Formos Software Development
Wrap Up © 2009 Formos Software Development
Clojure • 1.0 release: May 4 2009 • Simple, regular syntax • Improves on Lisp: vectors, maps, sets • Fully integrates with Java http://www.clojure.org • Impressive functional & concurrency support • Many features not covered here © 2009 Formos Software Development
Stuart Halloway Pragmatic Bookshelf http://pragprog.com/titles/shcloj/programming-clojure © 2009 Formos Software Development
http://jnb.ociweb.com/jnb/jnbMar2009.html © 2009 Formos Software Development
Object Oriented Copyright © A. Lipson 2003 Copyright © 2007 Alan Chia http://www.andrewlipson.com/escher/relativity.html http://flickr.com/photos/seven13avenue/2080281038/ © 2009 Formos Software Development
Object Oriented Copyright © A. Lipson 2003 Copyright © 2007 Alan Chia http://www.andrewlipson.com/escher/relativity.html http://flickr.com/photos/seven13avenue/2080281038/ © 2009 Formos Software Development
Functional © 2009 Formos Software Development
Functional Image © 2007 Woodley Wonderworks http://flickr.com/photos/wwworks/2222523486/ © 2009 Formos Software Development

Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)

  • 1.
    Clojure: Functional Concurrency for the JVM HowardM. Lewis Ship Director of Open Source Technology Formos Software Development howard.lewis.ship@formos.com © 2009 Formos Software Development
  • 2.
    Clojure: The Language © 2009 Formos Software Development
  • 3.
    http://xkcd.com/297/ Clojure: The Language © 2009 Formos Software Development
  • 4.
    Rich Hickey © 2009 Formos Software Development
  • 5.
    Code is Data Quoted list of '(1 2 3) numbers (biggest 5 42) Function call Function definition (defn biggest "Find the maximum of two numbers" [x y] (if (> x y) x y)) © 2009 Formos Software Development
  • 6.
    Read Eval PrintLoop user=> (defn biggest "Find the maximum of two numbers" [x y] (if (> x y) x y)) #=(var user/biggest) user=> (biggest 5 42) 42 user=> (doc biggest) ------------------------- user/biggest ([x y]) Find the maximum of two numbers nil user=> '(1 2 3) (1 2 3) user=> '(biggest 5 42) (biggest 5 42) user=> (first '(biggest 5 42)) biggest user=> (eval '(biggest 5 42)) 42 © 2009 Formos Software Development
  • 7.
    There Is NoInterpreter Source Code Repl Input Clojure User Classes Java Evaluator Compiler Clojure Source Files Java Libraries JVM Operating System © 2009 Formos Software Development
  • 8.
    Clojure Literals user=> 42 42 user=> "A Clojure String" "A Clojure String" user=> nil nil user=> :balance :balance user=> true true user=> false false © 2009 Formos Software Development
  • 9.
    Clojure Literals user=> 5 5 user=> 5.001 5.001 user=> 22/7 22/7 user=> (* 2 22/7) 44/7 user=> (* 100000 100000 100000) 1000000000000000 user=> (+ 5. 0.000000000000000001) 5.0 user=> (+ 5.0M 0.000000000000000001M) 5.000000000000000001M © 2009 Formos Software Development
  • 10.
    Java Interop factory.setNamespaceAware(true) (.setNamespaceAware factory true) new StringBuffer() (new StringBuffer) (StringBuffer.) factory.newSAXParser().parse(src, handler) (.. factory newSAXParser (parse src handler)) MyObject.ivar = "foo"; (set! (. MyObject ivar) "foo") © 2009 Formos Software Development
  • 11.
    Java Interop frame.add(panel, BorderLayout.CENTER); frame.add(greetButton,BorderLayout.SOUTH); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); (doto frame (.add panel BorderLayout/CENTER) (.add greet-button BorderLayout/SOUTH) (.pack) (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) (.setVisible true)) © 2009 Formos Software Development
  • 12.
    Clojure Collections: Lists lst user=> (def lst `(1 2 3)) #=(var user/lst) user=> lst (1 2 3) user=> (first lst) 1 1 user=> (rest lst) (2 3) user=> (conj lst 4) 2 (4 1 2 3) user=> (cons 4 lst) (4 1 2 3) 3 © 2009 Formos Software Development
  • 13.
    Clojure Collections: Lists 4 lst user=> (def lst `(1 2 3)) #=(var user/lst) user=> lst (1 2 3) user=> (first lst) 1 1 user=> (rest lst) (2 3) user=> (conj lst 4) 2 (4 1 2 3) user=> (cons 4 lst) (4 1 2 3) 3 © 2009 Formos Software Development
  • 14.
    Clojure Collections: Vectors user=> (def v [:moe :larry :curly]) #=(var user/v) user=> v [:moe :larry :curly] user=> (first v) :moe user=> (rest v) (:larry :curly) user=> (conj v :shemp) [:moe :larry :curly :shemp] user=> (cons :shemp v) (:shemp :moe :larry :curly) user=> v [:moe :larry :curly] user=> (v 1) :larry vector is a function of its indexes © 2009 Formos Software Development
  • 15.
    Clojure Collections: Map user=> (def m {:first-name "Howard" :last-name "Lewis Ship"}) #=(var user/m) user=> m {:last-name "Lewis Ship", :first-name "Howard"} user=> (get m :last-name) "Lewis Ship" map is a user=> (m :last-name) function of "Lewis Ship" its keys user=> (assoc m :company "Formos") {:company "Formos", :last-name "Lewis Ship", :first-name "Howard"} user=> m {:last-name "Lewis Ship", :first-name "Howard"} user=> (:first-name m) "Howard" user=> (:ssn m) nil Keywords are functions, too! © 2009 Formos Software Development
  • 16.
    Clojure Collections: Sets user=> (def s #{"Howard" "Suzanne" "Molly" "Jim"}) #=(var user/s) user=> s #{"Howard" "Jim" "Molly" "Suzanne"} user=> (contains? s "Howard") true user=> (contains? s "howard") false set is a user=> (s "Howard") function of "Howard" its elements user=> (s "Rhys") nil user=> (conj s "Howard") #{"Howard" "Jim" "Molly" "Suzanne"} user=> (conj s "Scott") #{"Howard" "Jim" "Molly" "Suzanne" "Scott"} © 2009 Formos Software Development
  • 17.
    © 2009 FormosSoftware Development
  • 18.
    ❝For alumni ofother languages, beginning to use Lisp may be like stepping onto a skating rink for the first time. It’s actually much easier to get around on ice than it is on dry land—if you use skates. Till then you will be left wondering what people see in this sport.❞ Paul Graham © 2009 Formos Software Development
  • 19.
    Functional Programming © 2009 Formos Software Development
  • 20.
    Functional Programming © 2009 Formos Software Development
  • 21.
    © 2009 FormosSoftware Development
  • 22.
    No Mutable State© 2009Formos Software Development
  • 23.
    © 2009 FormosSoftware Development
  • 24.
    No Side Effects © 2009 Formos Software Development
  • 25.
    © 2009 FormosSoftware Development
  • 26.
    First Class Functions © 2009 Formos Software Development
  • 27.
    © 2009 FormosSoftware Development
  • 28.
    Functional Composition © 2009 Formos Software Development
  • 29.
    Functional Programming inJava public void saveOrUpdate(final Employee employee) { HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException,SQLException { session.saveOrUpdate(employee); return null; } }; hibernateTemplate.execute(callback); } Outer function controls the SwingUtilities.invokeLater(new Runnable() context: { public void run() • Thread { • Exception handling progressBar.setValue(progressBar.getValue() + 1); } • Parameters }); © 2009 Formos Software Development
  • 30.
    Functional Java Collections publicinterface Predicate<T> { boolean accept(T value); } public static <T> Collection<T> filter(Predicate<T> pred, Collection<T> coll) { Collection<T> out = new ArrayList<T>(); for (T item : coll) { if (pred.accept(item)) out.add(item); } return out; } return CollectionUtils.filter(new Predicate<String>() { public boolean accept(String value) { return !value.startsWith("."); } }, names); © 2009 Formos Software Development
  • 31.
    Functional Clojure Collections Function Anonymous parameter function (filter #(not (.startsWith % ".")) names) Member access form user=> (def names ["fred" "barney" ".hidden" "wilma"]) #=(var user/names) user=> (filter #(not (.startsWith % ".")) names) ("fred" "barney" "wilma") user=> (remove #(.startsWith % ".") names) ("fred" "barney" "wilma") user=> © 2009 Formos Software Development
  • 32.
    First Class Functions (filter #(not (.startsWith % ".")) names) function as parameter to function (defn require-extension [ext] (fn [file-name] (= ext (last (split-string file-name "."))))) function as return value (defn filter-by-extension [ext coll] (filter (require-extension ext) coll)) composing functions © 2009 Formos Software Development
  • 33.
    Bridging Java andClojure SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(progressBar.getValue() + 1); } }); Invoke static method (SwingUtilities/invokeLater #(.setValue progressBar (inc (.getValue progressBar)))) Clojure functions implement: • Runnable • Callable • Comparator © 2009 Formos Software Development
  • 34.
    Life without thefor loop public static int sum(int[] vals) { int total = 0; col for (int val : vals) total += val; return total; } x y (defn sum z [col] (reduce + 0 col)) ➠ 0 + col[0] + col[1] + col[2] ... © 2009 Formos Software Development
  • 35.
    Life without thefor loop public static int sum(int[] vals) { int total = 0; col for (int val : vals) total += val; return total; } x y (defn sum z [col] (reduce + 0 col)) ➠ 0 + col[0] + col[1] + col[2] ... ➠ (+ 0 (first col)) (+ (+ 0 (first col)) (first (rest col))) (+ (+ (+ 0 (first col)) (first (rest col))) (first (rest (rest col)))) ... © 2009 Formos Software Development
  • 36.
    Life without thefor loop public static String[] formatDoubles(double[] inputs) { String[] output = new String[inputs.length]; for (int i = 0; i < input.length; i++) output[i] = String.format("%9.2f", inputs[i]); return output; } (defn format-doubles f [col] (map #(format "%9.2f" %) col)) f Apply function to each item, forming f new seq user=> (format-doubles '(2.5 3.7 -22.7)) (" 2.50" " 3.70" " -22.70") © 2009 Formos Software Development
  • 37.
    for: list comprehension user=>(range 0 5) (0 1 2 3 4) user=> (for [x (range 0 10) :when (even? x)] x) (0 2 4 6 8) user=> (for [x (range 1 5) y (range 0 x)] [x y]) ([1 0] [2 0] [2 1] [3 0] [3 1] [3 2] [4 0] [4 1] [4 2] [4 3]) © 2009 Formos Software Development
  • 38.
    for: list comprehension user=>(range 0 5) (0 1 2 3 4) user=> (for [x (range 0 10) :when (even? x)] x) (0 2 4 6 8) user=> (for [x (range 1 5) y (range 0 x)] [x y]) ([1 0] [2 0] [2 1] [3 0] [3 1] [3 2] [4 0] [4 1] [4 2] [4 3]) (defn convert-attributes-to-tokens [attrs] (for [x (range (.getLength attrs))] (let [uri (.getURI attrs x) name (.getLocalName attrs x) value (.getValue attrs x) token (…) ] token))) © 2009 Formos Software Development
  • 39.
    Laziness is a Virtue Image © 2007 Jon Fife http://flickr.com/photos/good-karma/577632972/ © 2009 Formos Software Development
  • 40.
    Laziness is a Virtue user=> (take 20 (iterate inc 1)) (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) user=> (take 20 (map * (iterate inc 1) (iterate inc 1))) (1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400) Image © 2007 Jon Fife http://flickr.com/photos/good-karma/577632972/ © 2009 Formos Software Development
  • 41.
    Laziness © 2009 Formos Software Development
  • 42.
    Java: Data Encapsulatedin Objects Person Person Person Person firstName: "Howard" firstName: "Scott" firstName: "Molly" firstName: "David" lastName: "Lewis Ship" lastName: "Simon" lastName: "Newman" lastName: "Goldman" age: 42 age: 44 age: 29 age: 42 public double averageAge(Collection<Person> persons) { double total = 0.0; for (Person p : persons) total += p.getAge(); return total / persons.size(); } © 2009 Formos Software Development
  • 43.
    Clojure: Data inTransformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> © 2009 Formos Software Development
  • 44.
    Clojure: Data inTransformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> (defn avg-age [coll] (/ (apply + (map :age coll)) (count coll))) © 2009 Formos Software Development
  • 45.
    Clojure: Data inTransformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> (defn avg-age [coll] (/ (apply + (map :age coll)) (count coll))) (defn avg (defn avg-age [f coll] [coll] (/ (apply + (map f coll))) (count coll))) (avg :age coll)) © 2009 Formos Software Development
  • 46.
    Clojure: Data inTransformable Collections :first-name Howard :first-name Scott :first-name Molly { :last-name Lewis Ship } { :last-name Simon }{ :last-name Newman } :age 42 :age 44 :age 29 user=> persons [{:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44} {:first-name "Molly", :last-name "Newman", :age 29}] user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> (defn avg-age [coll] (/ (apply + (map :age coll)) (count coll))) (defn avg (defn avg-age [f coll] [coll] (/ (apply + (map f coll))) (count coll))) (avg :age coll)) (avg #(count (:last-name %)) persons) © 2009 Formos Software Development
  • 47.
    © 2009 FormosSoftware Development
  • 48.
    ❝Somehow the ideaof reusability got attached to object-oriented programming in the 1980s, and no amount of evidence to the contrary seems to be able to shake it free.❞ Paul Graham © 2009 Formos Software Development
  • 49.
    Clojure Concurrency © 2009 Formos Software Development
  • 50.
    Solving Deadlocks: Timeout &Retry © 2009 Formos Software Development
  • 51.
    Solving Deadlocks: Specific Lock Order © 2009 Formos Software Development
  • 52.
    Solving Deadlocks: Coarse Locks Image© 2008 Marcin Wichary http://flickr.com/photos/mwichary/2222776430/ © 2009 Formos Software Development
  • 53.
    Locks are the Enemy Image © 2007 James Manners http://flickr.com/photos/jmanners/443421045/ © 2009 Formos Software Development
  • 54.
    Clojure: Software TransactionalMemory (def savings (ref 1000.)) (def checking (ref 2000.)) (def mm (ref 7000.)) (defn transfer "Transaction to transfer money from one account to another." [from to amount] (dosync (alter from - amount) (alter to + amount))) (transfer checking savings 500.) (transfer mm checking 300.) - 500. + 500. - 300. + 300. Checking 1000. 500. 500. Savings 2000. 2500. 2800. 2800. MM 7000. 6700. 6700. © 2009 Formos Software Development
  • 55.
    Retries: Transactions AreSpeculative (def savings (ref 1000.)) (def checking (ref 2000.)) (def mm (ref 7000.)) (defn transfer "Transaction to transfer money from one account to another." [from to amount] (dosync (alter from - amount) (alter to + amount))) (transfer checking savings 500.) (transfer mm checking 300.) + 300. - 500. - 300. + 500. - 300. + 300. Checking 1000. 500. X 500. Savings 2000. 2500. 2300. 2500. 2800. 2800. MM 7000. 6700. 7000. 6700. 6700. © 2009 Formos Software Development
  • 56.
    © 2009 FormosSoftware Development
  • 57.
    No Blocking © 2009 Formos Software Development
  • 58.
    © 2009 FormosSoftware Development
  • 59.
    No Locks © 2009 Formos Software Development
  • 60.
    © 2009 FormosSoftware Development
  • 61.
    Persistent Collections © 2009 Formos Software Development
  • 62.
    Managing Mutation • What can change? • Reference types: atom, var, agent, ref • When can they change? • When are changes visible to other threads? Image © 2008 Daniel Chan http://flickr.com/photos/chanchan222/2847443980/ © 2009 Formos Software Development
  • 63.
    Atoms • Shared, Global •Changes are atomic, synchronous, & non-blocking • (swap!): Pass value to function yielding new value • (reset!): Force new value, regardless of existing value user=> (def queue (atom [])) #'user/queue user=> @queue [] user=> (swap! queue conj {:parse "http://www.clojure.org/"}) [{:parse "http://www.clojure.org/"}] user=> @queue [{:parse "http://www.clojure.org/"}] user=> (reset! queue []) [] user=> @queue [] user=> © 2009 Formos Software Development
  • 64.
    Vars — Per-ThreadMutables • (def) sets global binding • (binding) to set up a per-thread override Image © 2005 Jack Keene http://www.flickr.com/photos/whatknot/3118124/ • (set!) if per-thread binding user=> (def x 1) #=(var user/x) user=> x 1 user=> (defn manipulate-x [] (binding [x 2] (printf "Local x is %d" x) (set! x 3) (printf "nLocal x is now %dn" x))) #=(var user/manipulate-x) user=> (.run (Thread. manipulate-x)) Local x is 2 Local x is now 3 nil user=> x 1 © 2009 Formos Software Development
  • 65.
    Interfacing with JavaAPIs (def *tokens*) (defn add-token [token] (set! *tokens* (conj *tokens* token))) (def sax-handler (proxy [DefaultHandler] [] (startElement [uri local-name q-name attrs] (flush-text) (add-token …)) … )) (defn tokenize-xml [src] (binding [*tokens* []] (let [factory (SAXParserFactory/newInstance)] (.setNamespaceAware factory true) (.. factory newSAXParser (parse src sax-handler)) *tokens*))) © 2009 Formos Software Development
  • 66.
    Everything's a Var! Function Namespace Var Value ... Symbol such as x or map © 2009 Formos Software Development
  • 67.
    Functions are storedin Vars user=> (defn say-hello [] (println "Hello")) #'user/say-hello user=> (say-hello) Hello nil user=> (binding [say-hello #(println "Goodbye")] (say-hello)) Goodbye nil user=> (say-hello) Hello nil user=> © 2009 Formos Software Development
  • 68.
    Agents — SingleThread Writes user=> (def savings (agent 1000.)) #=(var user/savings) user=> (def checking (agent 2000.)) #=(var user/checking) user=> @savings 1000 user=> @checking 2000 user=> (send savings - 300.) #<clojure.lang.Agent@c3233b> user=> (send checking + 300.) #<clojure.lang.Agent@67e5a7> user=> @savings 700 user=> @checking 2300 •Asynchonous •Single threaded •Non-transactional © 2009 Formos Software Development
  • 69.
    Refs — SoftwareTransactional Memory (def savings (ref 1000.)) (def checking (ref 2000.)) (def mm (ref 7000.)) (defn transfer "Transaction to transfer money from one account to another." [from to amount] (dosync (alter from - amount) (alter to + amount))) (transfer checking savings 500.) (transfer mm checking 300.) user=> @checking 2000 user=> @savings 1000 user=> (transfer checking savings 500.) 1500 user=> @checking 1500 user=> @savings 1500 user=> (ref-set savings 2000.) java.lang.IllegalStateException: No transaction running (NO_SOURCE_FILE:0) © 2009 Formos Software Development
  • 70.
    Concurrency Notes • Allreference types can have a validator function • All refs can have a watcher: an agent notified of changes • (send) inside (dosync) waits until successful completion • No guarantees when calling Java objects © 2009 Formos Software Development
  • 71.
    © 2009 FormosSoftware Development
  • 72.
    ❝The key toperformance is elegance, not battalions of special cases.❞ Jon Bentley and Doug McIlroy © 2009 Formos Software Development
  • 73.
    Wrap Up © 2009 Formos Software Development
  • 74.
    Clojure • 1.0 release:May 4 2009 • Simple, regular syntax • Improves on Lisp: vectors, maps, sets • Fully integrates with Java http://www.clojure.org • Impressive functional & concurrency support • Many features not covered here © 2009 Formos Software Development
  • 75.
    Stuart Halloway Pragmatic Bookshelf http://pragprog.com/titles/shcloj/programming-clojure © 2009 Formos Software Development
  • 76.
    http://jnb.ociweb.com/jnb/jnbMar2009.html © 2009 Formos Software Development
  • 77.
    Object Oriented Copyright ©A. Lipson 2003 Copyright © 2007 Alan Chia http://www.andrewlipson.com/escher/relativity.html http://flickr.com/photos/seven13avenue/2080281038/ © 2009 Formos Software Development
  • 78.
    Object Oriented Copyright ©A. Lipson 2003 Copyright © 2007 Alan Chia http://www.andrewlipson.com/escher/relativity.html http://flickr.com/photos/seven13avenue/2080281038/ © 2009 Formos Software Development
  • 79.
    Functional © 2009 Formos Software Development
  • 80.
    Functional Image © 2007Woodley Wonderworks http://flickr.com/photos/wwworks/2222523486/ © 2009 Formos Software Development