Clojure: Functional Concurrency for the JVM Howard M. Lewis Ship TWD Consulting hlship@gmail.com 1 © 2010 Howard Lewis Ship
Clojure: Why Functional Programming Matters Howard M. Lewis Ship TWD Consulting hlship@gmail.com 2 © 2010 Howard Lewis Ship
Clojure: Towards the Essence of Programming Howard M. Lewis Ship TWD Consulting hlship@gmail.com 3 © 2010 Howard Lewis Ship
essence noun the intrinsic nature or indispensable quality of something, esp. something abstract, that determines its character : conflict is the essence of drama. 4 © 2010 Howard Lewis Ship
Mainstream Programming Applications Frameworks Libraries Language Operating System 5 © 2010 Howard Lewis Ship
Ceremony vs. Essence 6 © 2010 Howard Lewis Ship
Is your language ... 7 © 2010 Howard Lewis Ship
… holding you back? 8 © 2010 Howard Lewis Ship
Java: Data Encapsulated in Objects Stock Stock Stock ticker: AAPL ticker: MSFT ticker: ORCL lastTrade: 203.25 lastTrade: 29.12 lastTrade: 21.90 open: 204.50 open: 29.08 open: 21.83 shares: 100 shares: 50 shares: 200 public static void sortByLastTrade(List<Stock> portfolio) { Comparator<Stock> c = new Comparator<Stock>() { public int compare(Stock o1, Stock o2) { public static void sortByOpen(List<Stock> portfolio) return o1.getLastTrade() - o2.getLastTrade(); { } Comparator<Stock> c = new Comparator<Stock>() }; { public int compare(Stock o1, Stock o2) Collections.sort(portfolio, c); { } return o1.getOpen() - o2.getOpen(); } }; Collections.sort(portfolio, c); } 9 © 2010 Howard Lewis Ship
Clojure: Data in Maps and Lists :ticker AAPL :ticker MSFT :ticker ORCL :last-trade 203.25 :last-trade 29.12 :last-trade 21.90 { :open 204.50 } { :open 29.08 }{ :open 21.83 } :shares 100 :shares 50 :shares 200 user=> portfolio [{:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}] user=> (sort-by :last-trade portfolio) ({:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100}) user=> (sort-by :shares portfolio) ({:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}) user=> 10 © 2010 Howard Lewis Ship
Functional JavaScript var portfolio = [ { ticker: "AAPL", lastTrade: 203.25, open: 204.50}, { ticker: "MSFT", lastTrade: 29.12, open: 29.08 }, { ticker: "ORCL", lastTrade: 21.90, open: 21.83 } ] portfolio.sortBy(function (stock) { return stock.lastTrade }).toJSON() [{"ticker": "ORCL", "lastTrade": 21.9, "open": 21.83}, {"ticker": "MSFT", "lastTrade": 29.12, "open": 29.08}, {"ticker": "AAPL", "lastTrade": 203.25, "open": 204.5}] 11 © 2010 Howard Lewis Ship
Clojure: The Language 12 © 2010 Howard Lewis Ship
Reading Lisp (defn render-json "Renders JSON content (typically, a map or a seq) as the response. The response content type is set to "application/json". Returns true." [env json-value] (let [response (-> env :servlet-api :response)] (.setContentType response "application/json") (with-open [writer (.getWriter Aargh! response)] (binding [*out* writer] (print-json json-value)))) true) 13 © 2010 Howard Lewis Ship
Structural View defn render-json env json-value let response -> env :servlet-api :response .setContentType response "application/json" with-open writer .getWriter response binding *out* writer print-json json-value 14 © 2010 Howard Lewis Ship
Java: Complex Structure int f = 9 * c / 5 + 32; Higher precendence, then left to right: ((9 * c) / 5) + 32 + (+ / 32 (/ (* 9 c) * 5 5) 32) 9 c 15 © 2010 Howard Lewis Ship
Clojure: Form Is Structure 16 © 2010 Howard Lewis Ship
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)) 17 © 2010 Howard Lewis Ship
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 18 © 2010 Howard Lewis Ship
REPL and Compilation Source Code Repl Input Clojure User Classes Java Evaluator Compiler Clojure Source Files Java Libraries JVM Operating System 19 © 2010 Howard Lewis Ship
Clojure Literals user=> "A Clojure String" "A Clojure String" user=> space space user=> A A user=> nil Java null nil user=> :balance :balance user=> true true user=> false false 20 © 2010 Howard Lewis Ship
Numeric Literals user=> 5 5 user=> 5.001 5.001 Ratio user=> 22/7 22/7 user=> (* 2 22/7) 44/7 user=> (* 100000 100000 100000) BigInteger 1000000000000000 user=> (+ 5. 0.000000000000000001) 5.0 user=> (+ 5.0M 0.000000000000000001M) 5.000000000000000001M BigDecimal 21 © 2010 Howard Lewis Ship
Java Interop: Function Calls (.method-name receiver arguments…) factory.setNamespaceAware(true) factory.newSaxParser().parse(src, handler) ( .. factory newSaxParser ( (.. (parse src handler)) )) 22 © 2010 Howard Lewis Ship
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 23 © 2010 Howard Lewis Ship
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] 24 © 2010 Howard Lewis Ship
Clojure Collections: Maps 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" user=> (assoc m :company "TWD") {:company "TWD", :last-name "Lewis Ship", :first-name "Howard"} user=> m {:last-name "Lewis Ship", :first-name "Howard"} user=> (get m:ssn) nil 25 © 2010 Howard Lewis Ship
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 user=> (conj s "Howard") #{"Howard" "Jim" "Molly" "Suzanne"} user=> (conj s "Scott") #{"Howard" "Jim" "Molly" "Suzanne" "Scott"} 26 © 2010 Howard Lewis Ship
Functional Programming 27 © 2010 Howard Lewis Ship
My First Program No it 10 X = 1 doesn't! 20 PRINT X 30 X = X + 1 40 GOTO 20 28 © 2010 Howard Lewis Ship
No Mutable State 29 © 2010 Howard Lewis Ship
No Side Effects 30 © 2010 Howard Lewis Ship
Purity 31 © 2010 Howard Lewis Ship
Simplicity Predictability Testability 32 © 2010 Howard Lewis Ship
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("."); Essence } }, names); 33 © 2010 Howard Lewis Ship
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=> 34 © 2010 Howard Lewis Ship
Composing Functions (filter #(not (.startsWith % ".")) names) function as parameter to function (defn require-extension [ext] ❝Closure Oriented (fn [file-name] (= ext (last (split-string file-name "."))))) Programming❞ function as return value (filter (require-extension "gz") names) composing functions 35 © 2010 Howard Lewis Ship
Java: Iterative Steps 36 © 2010 Howard Lewis Ship
Java: Data Encapsulated in Objects Stock Stock Stock ticker: AAPL ticker: MSFT ticker: ORCL lastTrade: 203.25 lastTrade: 29.12 lastTrade: 21.90 open: 204.50 open: 29.08 open: 21.83 shares: 100 shares: 50 shares: 200 public static List<Double> getOpens(List<Stock> portfolio) { List<Double> result = new ArrayList<Double>(); for (Stock stock : portfolio) { result.add(stock.getOpen()); } return result; } 37 © 2010 Howard Lewis Ship
Clojure: Flow of Transformations 38 © 2010 Howard Lewis Ship
Clojure: Data in Transformable Collections :ticker AAPL :ticker MSFT :ticker ORCL :last-trade 203.25 :last-trade 29.12 :last-trade 21.90 { :open 204.50 } { :open 29.08 }{ :open 21.83 } :shares 100 :shares 50 :shares 200 user=> portfolio [{:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}] user=> (map #(get % :open) portfolio) (204.50M 29.08M 21.83M) user=> 39 © 2010 Howard Lewis Ship
Clojure: Data in Transformable Collections :ticker AAPL :ticker MSFT :ticker ORCL :last-trade 203.25 :last-trade 29.12 :last-trade 21.90 { :open 204.50 } { :open 29.08 }{ :open 21.83 } :shares 100 :shares 50 :shares 200 user=> portfolio [{:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}] user=> (apply + (map #(* (:last-trade %) (:shares %)) portfolio)) 26161.00M user=> (map #(assoc % :delta (- (% :last-trade) (% :open))) portfolio) ({:delta -1.25M, :ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:delta 0.04M, :ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:delta 0.07M, :ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}) user=> 40 © 2010 Howard Lewis Ship
map La zy f keywords acts as functions (:keyword map) == (get map :keyword) user=> (map :open portfolio) (204.5 29.08 21.83) user=> (defn last-trade-value [stock] (* (:last-trade stock) (:shares stock))) #'user/last-trade-value user=> (map last-trade-value portfolio) (20325.00M 1456.00M 4380.00M) user=> 41 © 2010 Howard Lewis Ship
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) 42 © 2010 Howard Lewis Ship
43 © 2010 Howard Lewis Ship
map La zy f N seqs ➠ N parameters user=> (map #(assoc %1 :sort-index %2) (reverse (sort-by :last-trade portfolio)) (iterate inc 0)) ({:sort-index 0, :ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:sort-index 1, :ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:sort-index 2, :ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}) user=> 44 © 2010 Howard Lewis Ship
reduce f user=> (map #(* (% :last-trade) (% :shares)) portfolio) (20325.00M 1456.00M 4380.00M) user=> (reduce + (map #(* (% :last-trade) (% :shares)) portfolio)) 26161.00M user=> (reduce + (map #(* (% :last-trade) (% :shares)) portfolio)) (reduce + '(20325.00M 1456.00M 4380.00M)) (+ (+ 20325.00M 1456.00M) 4380.00M) (+ 21781.00M 4380.00M) 26161.00M 45 © 2010 Howard Lewis Ship
reduce f user=> (def input-string "Clojure is a fascinating language with unique capabilities and total integration with Java.") #'user/input-string user=> (seq input-string) (C l o j u r e space i s space a space f a s c i n a t i n g space l a n g u a g e space w i t h space u n i q u e space c a p a b i l i t i e s space a n d space t o t a l space i n t e g r a t i o n space w i t h space J a v a .) user=> (reduce (fn [m k] (update-in m [k] #(inc (or % 0)))) Optional initial value {} (seq input-string)) {space 12, a 12, b 1, C 1, c 2, d 1, e 5, f 1, g 4, h 2, i 11, J 1, j 1, l 4, . 1, n 7, o 3, p 1, q 1, r 2, s 3, t 8, u 4, v 1, w 2} user=> 46 © 2010 Howard Lewis Ship
filter / remove La zy f ? user=> (remove #(< 100 (% :shares)) portfolio) ({:ticker "AAPL", :last-trade 203.25, :open 204.5, :shares 100} {:ticker "ORCL", :last-trade 21.9, :open 21.83, :shares 57}) user=> (filter #(< 100 (% :shares)) portfolio) ({:ticker "MSFT", :last-trade 29.12, :open 29.08, :shares 125}) user=> 47 © 2010 Howard Lewis Ship
for: list comprehension La zy user=> (for [suit [:hearts :clubs :spades :diamonds] value (range 1 4)] [suit value]) ([:hearts 1] [:hearts 2] [:hearts 3] [:clubs 1] [:clubs 2] [:clubs 3] [:spades 1] [:spades 2] [:spades 3] [:diamonds 1] [:diamonds 2] [:diamonds 3]) user=> (for [x (range 0 4) y (range 0 (inc x))] [x y]) ([0 0] [1 0] [1 1] [2 0] [2 1] [2 2] [3 0] [3 1] [3 2] [3 3]) user=> (for [x (range 0 9) :when (odd? x) y (range 1 (inc x))] [x y]) ([1 1] [3 1] [3 2] [3 3] [5 1] [5 2] [5 3] [5 4] [5 5] [7 1] [7 2] [7 3] [7 4] [7 5] [7 6] [7 7]) user=> 48 © 2010 Howard Lewis Ship
❝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 © 2010 Howard Lewis Ship
Language Ownership 50 © 2010 Howard Lewis Ship
Who Owns the Java Language? James Gosling Mark Reinhold 51 © 2010 Howard Lewis Ship
Not You 52 © 2010 Howard Lewis Ship
Control of the Compiler Source Code Repl Input Clojure User Classes Java Evaluator Compiler Clojure Source Files Java Libraries JVM Operating System 53 © 2010 Howard Lewis Ship
Who Owns Clojure? Rich Hickey 54 © 2010 Howard Lewis Ship
Clojure: written in Clojure 55 © 2010 Howard Lewis Ship
Short Circuits && stops with first false if (person.isPharaoh() && (if person.isDead() && (all-true constructPyramid()) (.isPharaoh person) { (.isDead person) person.bury(); (construct-pyramid)) } (.bury person)) all parameters to function all-true evaluated first public static boolean allTrue(boolean... inputs) { for (boolean input : inputs) { if (!input) return false; } return true; } 56 © 2010 Howard Lewis Ship
Caution: Head Exploding Zone 57 © 2010 Howard Lewis Ship
Clojure Macros short circuit at first false/nil Reader (and a b c d) Evaluator macro expansion and macro Bytecode (let [and__4422__auto__ a] (if and__4422__auto__ Generation (and b c d) and__4422__auto__)) Recursively expanded 58 © 2010 Howard Lewis Ship
Macros ➠ Special Forms Code Forms Macro Expansion def if let fn . … Bytecode Generation 59 © 2010 Howard Lewis Ship
Macros are Special Functions (defmacro and "Evaluates exprs one at a time, from left to right. If a form returns logical false (nil or false), and returns that value and doesn't evaluate any of the other expressions, otherwise it returns the value of the last expr. (and) returns true." ([] true) ([x] x) ([x & next] `(let [and# ~x] (if and# (and ~@next) and#)))) • `(…) — Syntax Quote (allowing replacements) • and# — Generate a new unique symbol • ~x — Unquote x • ~@next — Unquote next and splice in multiple values 60 © 2010 Howard Lewis Ship
Macros are Special Functions (defmacro and ([] true) ([x] x) (and a b c d) ([x & next] `(let [and# ~x] (if and# (and ~@next) and#)))) •x➠a • next ➠ '(b c d) • Inside syntax quote: • and# ➠ and__4422__auto__ • ~x ➠ a • ~next ➠ '(b c d) • (and ~@next) ➠ (and b c d) 61 © 2010 Howard Lewis Ship
Simplifying Boilerplate Code public void testLink() { IMocksControl control = EasyMock.createControl(); HttpServletRequest request = control.newMock(HttpServletRequest.class); HttpServletResponse response = control.newMock(HttpServletResponse.class); EasyMock.expect(request.getContextPath()).andReturn("/ctx"); EasyMock.expect(response.encodeURL("/ctx/accounts/list")).andReturn("*encoded*"); control.replay(); assertEquals(…, "*encoded*"); control.verify(); } (deftest test-link (with-mocks [request HttpServletRequest response HttpServletResponse] (:train (expect .getContextPath request "/ctx") (expect .encodeURL response "/ctx/accounts/list" "*encoded*")) (:test (is (= (link request response list-accounts-with-loop) "*encoded*"))))) 62 © 2010 Howard Lewis Ship
Embedded DSLs (defview root-index [env] :html [ :head [ :title [ "Cascade Blog" ] ] :body [ :h1 [ "Cascade Blog" ] :html :ul { :class "recent-postings" } [ (template-for [posting (recent-postings env)] :li [ (render-link env show-posting (posting :id) (posting :title)) ]) :head :body ] ] ]) :title :h1 :ul "Cascade Blog" "Cascade Blog" (template-for …) 63 © 2010 Howard Lewis Ship
Macro Expansions (defn list-items [coll] (template (format "%d items" (count coll)) :ul {:class :item-list} [ (template-for [item coll] :li [item]))) Expand simple Extend Clojure placeholder to language from executable code within Clojure (defn list-items [coll] (cascade.internal.viewbuilder/combine (format "%d items" (count coll)) (cascade.dom/element-node :ul {:class :item-list} (cascade.internal.viewbuilder/combine (for [item coll] (cascade.dom/element-node :li nil (cascade.internal.viewbuilder/combine item))))))) 64 © 2010 Howard Lewis Ship
❝More than anything else, I think it is the ability of Lisp programs to manipulate Lisp expressions that sets Lisp apart … when I hear people complain about Lisp's parentheses, it sounds to my ears like someone saying: "I tried one of those bananas, which you say are so delicious. The white part was ok, but the yellow part was very tough and tasted awful."❞ Paul Graham © 2010 Howard Lewis Ship
Wrap Up 66 © 2010 Howard Lewis Ship
essence noun the intrinsic nature or indispensable quality of something, esp. something abstract, that determines its character : conflict is the essence of drama. 67 © 2010 Howard Lewis Ship
Control is the Essence of Programming 68 © 2010 Howard Lewis Ship
Evaluation 69 © 2010 Howard Lewis Ship
If When How 70 © 2010 Howard Lewis Ship
Clojure • 1.1 release: Dec 31 2009 • Simple, regular syntax • Improves on Lisp: vectors, maps, sets http://www.clojure.org • Fully integrates with Java • Impressive functional & concurrency support • Many features not covered here 71 © 2010 Howard Lewis Ship
Stuart Halloway Pragmatic Bookshelf http://pragprog.com/titles/shcloj/programming-clojure 72 © 2010 Howard Lewis Ship
http://java.ociweb.com/mark/clojure/article.html 73 © 2010 Howard Lewis Ship
http://tapestryjava.blogspot.com 74 © 2010 Howard Lewis Ship
Object Oriented 75 © 2010 Howard Lewis Ship
Functional 76 © 2010 Howard Lewis Ship
Image Credits © 2007 Jon Fife http://flickr.com/photos/good-karma/577632972/ © 2007 Casey Marshall http://www.flickr.com/photos/rsdio/497112391/ © 2009 Howard M. Lewis Ship http://www.flickr.com/photos/hlship/3603090614/ © 2009 Andrew Baird http://www.flickr.com/photos/scruffy/3708615414/ © 2008 Miles Sabin http://www.flickr.com/photos/montpelier/2915114545/ © 2003 A. Lipson http://www.andrewlipson.com/escher/relativity.html © 2007 Alan Chia http://flickr.com/photos/seven13avenue/2080281038/ © 2007 Woodley Wonderworks http://flickr.com/photos/wwworks/2222523486/ © Randall Munroe http://xkcd.com/297/ © 2008 Manu Gómez http://www.flickr.com/photos/manugomi/2884678938/ © 2008 Marcin Wichary http://www.flickr.com/photos/mwichary/2827326852/ © 2006 Marvin (PA) http://www.flickr.com/photos/mscolly/145052885/ 77 © 2010 Howard Lewis Ship
Image Credits © 2007 John Kannenberg http://www.flickr.com/photos/jkannenberg/541057337/ © 2006 scott ogilvie http://www.flickr.com/photos/scottog/100582274/ © 2008 Ariel H. http://www.flickr.com/photos/fotosrotas/2730733412/ © 2006 John Ryan Brubaker http://www.flickr.com/photos/subconscience/297682093/ 78 © 2010 Howard Lewis Ship

Clojure: Towards The Essence of Programming

  • 1.
    Clojure: Functional Concurrency for the JVM HowardM. Lewis Ship TWD Consulting hlship@gmail.com 1 © 2010 Howard Lewis Ship
  • 2.
    Clojure: Why Functional Programming Matters Howard M.Lewis Ship TWD Consulting hlship@gmail.com 2 © 2010 Howard Lewis Ship
  • 3.
    Clojure: Towards the Essenceof Programming Howard M. Lewis Ship TWD Consulting hlship@gmail.com 3 © 2010 Howard Lewis Ship
  • 4.
    essence noun the intrinsic natureor indispensable quality of something, esp. something abstract, that determines its character : conflict is the essence of drama. 4 © 2010 Howard Lewis Ship
  • 5.
    Mainstream Programming Applications Frameworks Libraries Language Operating System 5 © 2010 Howard Lewis Ship
  • 6.
    Ceremony vs. Essence 6 © 2010 Howard Lewis Ship
  • 7.
    Is your language... 7 © 2010 Howard Lewis Ship
  • 8.
    … holding you back? 8 © 2010 Howard Lewis Ship
  • 9.
    Java: Data Encapsulatedin Objects Stock Stock Stock ticker: AAPL ticker: MSFT ticker: ORCL lastTrade: 203.25 lastTrade: 29.12 lastTrade: 21.90 open: 204.50 open: 29.08 open: 21.83 shares: 100 shares: 50 shares: 200 public static void sortByLastTrade(List<Stock> portfolio) { Comparator<Stock> c = new Comparator<Stock>() { public int compare(Stock o1, Stock o2) { public static void sortByOpen(List<Stock> portfolio) return o1.getLastTrade() - o2.getLastTrade(); { } Comparator<Stock> c = new Comparator<Stock>() }; { public int compare(Stock o1, Stock o2) Collections.sort(portfolio, c); { } return o1.getOpen() - o2.getOpen(); } }; Collections.sort(portfolio, c); } 9 © 2010 Howard Lewis Ship
  • 10.
    Clojure: Data inMaps and Lists :ticker AAPL :ticker MSFT :ticker ORCL :last-trade 203.25 :last-trade 29.12 :last-trade 21.90 { :open 204.50 } { :open 29.08 }{ :open 21.83 } :shares 100 :shares 50 :shares 200 user=> portfolio [{:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}] user=> (sort-by :last-trade portfolio) ({:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100}) user=> (sort-by :shares portfolio) ({:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}) user=> 10 © 2010 Howard Lewis Ship
  • 11.
    Functional JavaScript var portfolio= [ { ticker: "AAPL", lastTrade: 203.25, open: 204.50}, { ticker: "MSFT", lastTrade: 29.12, open: 29.08 }, { ticker: "ORCL", lastTrade: 21.90, open: 21.83 } ] portfolio.sortBy(function (stock) { return stock.lastTrade }).toJSON() [{"ticker": "ORCL", "lastTrade": 21.9, "open": 21.83}, {"ticker": "MSFT", "lastTrade": 29.12, "open": 29.08}, {"ticker": "AAPL", "lastTrade": 203.25, "open": 204.5}] 11 © 2010 Howard Lewis Ship
  • 12.
    Clojure: The Language 12 © 2010 Howard Lewis Ship
  • 13.
    Reading Lisp (defn render-json "Renders JSON content (typically, a map or a seq) as the response. The response content type is set to "application/json". Returns true." [env json-value] (let [response (-> env :servlet-api :response)] (.setContentType response "application/json") (with-open [writer (.getWriter Aargh! response)] (binding [*out* writer] (print-json json-value)))) true) 13 © 2010 Howard Lewis Ship
  • 14.
    Structural View defn render-json env json-value let response -> env :servlet-api :response .setContentType response "application/json" with-open writer .getWriter response binding *out* writer print-json json-value 14 © 2010 Howard Lewis Ship
  • 15.
    Java: Complex Structure intf = 9 * c / 5 + 32; Higher precendence, then left to right: ((9 * c) / 5) + 32 + (+ / 32 (/ (* 9 c) * 5 5) 32) 9 c 15 © 2010 Howard Lewis Ship
  • 16.
    Clojure: Form Is Structure 16 © 2010 Howard Lewis Ship
  • 17.
    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)) 17 © 2010 Howard Lewis Ship
  • 18.
    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 18 © 2010 Howard Lewis Ship
  • 19.
    REPL and Compilation Source Code Repl Input Clojure User Classes Java Evaluator Compiler Clojure Source Files Java Libraries JVM Operating System 19 © 2010 Howard Lewis Ship
  • 20.
    Clojure Literals user=> "A Clojure String" "A Clojure String" user=> space space user=> A A user=> nil Java null nil user=> :balance :balance user=> true true user=> false false 20 © 2010 Howard Lewis Ship
  • 21.
    Numeric Literals user=> 5 5 user=> 5.001 5.001 Ratio user=> 22/7 22/7 user=> (* 2 22/7) 44/7 user=> (* 100000 100000 100000) BigInteger 1000000000000000 user=> (+ 5. 0.000000000000000001) 5.0 user=> (+ 5.0M 0.000000000000000001M) 5.000000000000000001M BigDecimal 21 © 2010 Howard Lewis Ship
  • 22.
    Java Interop: FunctionCalls (.method-name receiver arguments…) factory.setNamespaceAware(true) factory.newSaxParser().parse(src, handler) ( .. factory newSaxParser ( (.. (parse src handler)) )) 22 © 2010 Howard Lewis Ship
  • 23.
    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 23 © 2010 Howard Lewis Ship
  • 24.
    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] 24 © 2010 Howard Lewis Ship
  • 25.
    Clojure Collections: Maps 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" user=> (assoc m :company "TWD") {:company "TWD", :last-name "Lewis Ship", :first-name "Howard"} user=> m {:last-name "Lewis Ship", :first-name "Howard"} user=> (get m:ssn) nil 25 © 2010 Howard Lewis Ship
  • 26.
    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 user=> (conj s "Howard") #{"Howard" "Jim" "Molly" "Suzanne"} user=> (conj s "Scott") #{"Howard" "Jim" "Molly" "Suzanne" "Scott"} 26 © 2010 Howard Lewis Ship
  • 27.
    Functional Programming 27 © 2010 Howard Lewis Ship
  • 28.
    My First Program No it 10 X = 1 doesn't! 20 PRINT X 30 X = X + 1 40 GOTO 20 28 © 2010 Howard Lewis Ship
  • 29.
    No Mutable State 29 © 2010 Howard Lewis Ship
  • 30.
    No Side Effects 30 © 2010 Howard Lewis Ship
  • 31.
    Purity 31 © 2010 Howard Lewis Ship
  • 32.
    Simplicity Predictability Testability 32 © 2010 Howard Lewis Ship
  • 33.
    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("."); Essence } }, names); 33 © 2010 Howard Lewis Ship
  • 34.
    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=> 34 © 2010 Howard Lewis Ship
  • 35.
    Composing Functions (filter #(not(.startsWith % ".")) names) function as parameter to function (defn require-extension [ext] ❝Closure Oriented (fn [file-name] (= ext (last (split-string file-name "."))))) Programming❞ function as return value (filter (require-extension "gz") names) composing functions 35 © 2010 Howard Lewis Ship
  • 36.
    Java: Iterative Steps 36 © 2010 Howard Lewis Ship
  • 37.
    Java: Data Encapsulatedin Objects Stock Stock Stock ticker: AAPL ticker: MSFT ticker: ORCL lastTrade: 203.25 lastTrade: 29.12 lastTrade: 21.90 open: 204.50 open: 29.08 open: 21.83 shares: 100 shares: 50 shares: 200 public static List<Double> getOpens(List<Stock> portfolio) { List<Double> result = new ArrayList<Double>(); for (Stock stock : portfolio) { result.add(stock.getOpen()); } return result; } 37 © 2010 Howard Lewis Ship
  • 38.
    Clojure: Flow ofTransformations 38 © 2010 Howard Lewis Ship
  • 39.
    Clojure: Data inTransformable Collections :ticker AAPL :ticker MSFT :ticker ORCL :last-trade 203.25 :last-trade 29.12 :last-trade 21.90 { :open 204.50 } { :open 29.08 }{ :open 21.83 } :shares 100 :shares 50 :shares 200 user=> portfolio [{:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}] user=> (map #(get % :open) portfolio) (204.50M 29.08M 21.83M) user=> 39 © 2010 Howard Lewis Ship
  • 40.
    Clojure: Data inTransformable Collections :ticker AAPL :ticker MSFT :ticker ORCL :last-trade 203.25 :last-trade 29.12 :last-trade 21.90 { :open 204.50 } { :open 29.08 }{ :open 21.83 } :shares 100 :shares 50 :shares 200 user=> portfolio [{:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}] user=> (apply + (map #(* (:last-trade %) (:shares %)) portfolio)) 26161.00M user=> (map #(assoc % :delta (- (% :last-trade) (% :open))) portfolio) ({:delta -1.25M, :ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:delta 0.04M, :ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:delta 0.07M, :ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}) user=> 40 © 2010 Howard Lewis Ship
  • 41.
    map La zy f keywords acts as functions (:keyword map) == (get map :keyword) user=> (map :open portfolio) (204.5 29.08 21.83) user=> (defn last-trade-value [stock] (* (:last-trade stock) (:shares stock))) #'user/last-trade-value user=> (map last-trade-value portfolio) (20325.00M 1456.00M 4380.00M) user=> 41 © 2010 Howard Lewis Ship
  • 42.
    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) 42 © 2010 Howard Lewis Ship
  • 43.
    43 © 2010 Howard Lewis Ship
  • 44.
    map La zy f N seqs ➠ N parameters user=> (map #(assoc %1 :sort-index %2) (reverse (sort-by :last-trade portfolio)) (iterate inc 0)) ({:sort-index 0, :ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:sort-index 1, :ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:sort-index 2, :ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}) user=> 44 © 2010 Howard Lewis Ship
  • 45.
    reduce f user=> (map #(* (% :last-trade) (% :shares)) portfolio) (20325.00M 1456.00M 4380.00M) user=> (reduce + (map #(* (% :last-trade) (% :shares)) portfolio)) 26161.00M user=> (reduce + (map #(* (% :last-trade) (% :shares)) portfolio)) (reduce + '(20325.00M 1456.00M 4380.00M)) (+ (+ 20325.00M 1456.00M) 4380.00M) (+ 21781.00M 4380.00M) 26161.00M 45 © 2010 Howard Lewis Ship
  • 46.
    reduce f user=> (def input-string "Clojure is a fascinating language with unique capabilities and total integration with Java.") #'user/input-string user=> (seq input-string) (C l o j u r e space i s space a space f a s c i n a t i n g space l a n g u a g e space w i t h space u n i q u e space c a p a b i l i t i e s space a n d space t o t a l space i n t e g r a t i o n space w i t h space J a v a .) user=> (reduce (fn [m k] (update-in m [k] #(inc (or % 0)))) Optional initial value {} (seq input-string)) {space 12, a 12, b 1, C 1, c 2, d 1, e 5, f 1, g 4, h 2, i 11, J 1, j 1, l 4, . 1, n 7, o 3, p 1, q 1, r 2, s 3, t 8, u 4, v 1, w 2} user=> 46 © 2010 Howard Lewis Ship
  • 47.
    filter / remove La zy f ? user=> (remove #(< 100 (% :shares)) portfolio) ({:ticker "AAPL", :last-trade 203.25, :open 204.5, :shares 100} {:ticker "ORCL", :last-trade 21.9, :open 21.83, :shares 57}) user=> (filter #(< 100 (% :shares)) portfolio) ({:ticker "MSFT", :last-trade 29.12, :open 29.08, :shares 125}) user=> 47 © 2010 Howard Lewis Ship
  • 48.
    for: list comprehension La zy user=> (for [suit [:hearts :clubs :spades :diamonds] value (range 1 4)] [suit value]) ([:hearts 1] [:hearts 2] [:hearts 3] [:clubs 1] [:clubs 2] [:clubs 3] [:spades 1] [:spades 2] [:spades 3] [:diamonds 1] [:diamonds 2] [:diamonds 3]) user=> (for [x (range 0 4) y (range 0 (inc x))] [x y]) ([0 0] [1 0] [1 1] [2 0] [2 1] [2 2] [3 0] [3 1] [3 2] [3 3]) user=> (for [x (range 0 9) :when (odd? x) y (range 1 (inc x))] [x y]) ([1 1] [3 1] [3 2] [3 3] [5 1] [5 2] [5 3] [5 4] [5 5] [7 1] [7 2] [7 3] [7 4] [7 5] [7 6] [7 7]) user=> 48 © 2010 Howard Lewis Ship
  • 49.
    ❝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 © 2010 Howard Lewis Ship
  • 50.
    Language Ownership 50 © 2010 Howard Lewis Ship
  • 51.
    Who Owns theJava Language? James Gosling Mark Reinhold 51 © 2010 Howard Lewis Ship
  • 52.
    Not You 52 © 2010 Howard Lewis Ship
  • 53.
    Control of theCompiler Source Code Repl Input Clojure User Classes Java Evaluator Compiler Clojure Source Files Java Libraries JVM Operating System 53 © 2010 Howard Lewis Ship
  • 54.
    Who Owns Clojure? Rich Hickey 54 © 2010 Howard Lewis Ship
  • 55.
    Clojure: written inClojure 55 © 2010 Howard Lewis Ship
  • 56.
    Short Circuits && stops with first false if (person.isPharaoh() && (if person.isDead() && (all-true constructPyramid()) (.isPharaoh person) { (.isDead person) person.bury(); (construct-pyramid)) } (.bury person)) all parameters to function all-true evaluated first public static boolean allTrue(boolean... inputs) { for (boolean input : inputs) { if (!input) return false; } return true; } 56 © 2010 Howard Lewis Ship
  • 57.
    Caution: Head Exploding Zone 57 © 2010 Howard Lewis Ship
  • 58.
    Clojure Macros short circuit at first false/nil Reader (and a b c d) Evaluator macro expansion and macro Bytecode (let [and__4422__auto__ a] (if and__4422__auto__ Generation (and b c d) and__4422__auto__)) Recursively expanded 58 © 2010 Howard Lewis Ship
  • 59.
    Macros ➠ SpecialForms Code Forms Macro Expansion def if let fn . … Bytecode Generation 59 © 2010 Howard Lewis Ship
  • 60.
    Macros are SpecialFunctions (defmacro and "Evaluates exprs one at a time, from left to right. If a form returns logical false (nil or false), and returns that value and doesn't evaluate any of the other expressions, otherwise it returns the value of the last expr. (and) returns true." ([] true) ([x] x) ([x & next] `(let [and# ~x] (if and# (and ~@next) and#)))) • `(…) — Syntax Quote (allowing replacements) • and# — Generate a new unique symbol • ~x — Unquote x • ~@next — Unquote next and splice in multiple values 60 © 2010 Howard Lewis Ship
  • 61.
    Macros are SpecialFunctions (defmacro and ([] true) ([x] x) (and a b c d) ([x & next] `(let [and# ~x] (if and# (and ~@next) and#)))) •x➠a • next ➠ '(b c d) • Inside syntax quote: • and# ➠ and__4422__auto__ • ~x ➠ a • ~next ➠ '(b c d) • (and ~@next) ➠ (and b c d) 61 © 2010 Howard Lewis Ship
  • 62.
    Simplifying Boilerplate Code publicvoid testLink() { IMocksControl control = EasyMock.createControl(); HttpServletRequest request = control.newMock(HttpServletRequest.class); HttpServletResponse response = control.newMock(HttpServletResponse.class); EasyMock.expect(request.getContextPath()).andReturn("/ctx"); EasyMock.expect(response.encodeURL("/ctx/accounts/list")).andReturn("*encoded*"); control.replay(); assertEquals(…, "*encoded*"); control.verify(); } (deftest test-link (with-mocks [request HttpServletRequest response HttpServletResponse] (:train (expect .getContextPath request "/ctx") (expect .encodeURL response "/ctx/accounts/list" "*encoded*")) (:test (is (= (link request response list-accounts-with-loop) "*encoded*"))))) 62 © 2010 Howard Lewis Ship
  • 63.
    Embedded DSLs (defviewroot-index [env] :html [ :head [ :title [ "Cascade Blog" ] ] :body [ :h1 [ "Cascade Blog" ] :html :ul { :class "recent-postings" } [ (template-for [posting (recent-postings env)] :li [ (render-link env show-posting (posting :id) (posting :title)) ]) :head :body ] ] ]) :title :h1 :ul "Cascade Blog" "Cascade Blog" (template-for …) 63 © 2010 Howard Lewis Ship
  • 64.
    Macro Expansions (defn list-items [coll] (template (format "%d items" (count coll)) :ul {:class :item-list} [ (template-for [item coll] :li [item]))) Expand simple Extend Clojure placeholder to language from executable code within Clojure (defn list-items [coll] (cascade.internal.viewbuilder/combine (format "%d items" (count coll)) (cascade.dom/element-node :ul {:class :item-list} (cascade.internal.viewbuilder/combine (for [item coll] (cascade.dom/element-node :li nil (cascade.internal.viewbuilder/combine item))))))) 64 © 2010 Howard Lewis Ship
  • 65.
    ❝More than anythingelse, I think it is the ability of Lisp programs to manipulate Lisp expressions that sets Lisp apart … when I hear people complain about Lisp's parentheses, it sounds to my ears like someone saying: "I tried one of those bananas, which you say are so delicious. The white part was ok, but the yellow part was very tough and tasted awful."❞ Paul Graham © 2010 Howard Lewis Ship
  • 66.
    Wrap Up 66 © 2010 Howard Lewis Ship
  • 67.
    essence noun the intrinsic natureor indispensable quality of something, esp. something abstract, that determines its character : conflict is the essence of drama. 67 © 2010 Howard Lewis Ship
  • 68.
    Control is the Essence of Programming 68 © 2010 Howard Lewis Ship
  • 69.
    Evaluation 69 © 2010 Howard Lewis Ship
  • 70.
    If When How 70 © 2010 Howard Lewis Ship
  • 71.
    Clojure • 1.1 release:Dec 31 2009 • Simple, regular syntax • Improves on Lisp: vectors, maps, sets http://www.clojure.org • Fully integrates with Java • Impressive functional & concurrency support • Many features not covered here 71 © 2010 Howard Lewis Ship
  • 72.
    Stuart Halloway Pragmatic Bookshelf http://pragprog.com/titles/shcloj/programming-clojure 72 © 2010 Howard Lewis Ship
  • 73.
  • 74.
    http://tapestryjava.blogspot.com 74 © 2010 Howard Lewis Ship
  • 75.
    Object Oriented 75 © 2010 Howard Lewis Ship
  • 76.
    Functional 76 © 2010 Howard Lewis Ship
  • 77.
    Image Credits © 2007 Jon Fife http://flickr.com/photos/good-karma/577632972/ © 2007 Casey Marshall http://www.flickr.com/photos/rsdio/497112391/ © 2009 Howard M. Lewis Ship http://www.flickr.com/photos/hlship/3603090614/ © 2009 Andrew Baird http://www.flickr.com/photos/scruffy/3708615414/ © 2008 Miles Sabin http://www.flickr.com/photos/montpelier/2915114545/ © 2003 A. Lipson http://www.andrewlipson.com/escher/relativity.html © 2007 Alan Chia http://flickr.com/photos/seven13avenue/2080281038/ © 2007 Woodley Wonderworks http://flickr.com/photos/wwworks/2222523486/ © Randall Munroe http://xkcd.com/297/ © 2008 Manu Gómez http://www.flickr.com/photos/manugomi/2884678938/ © 2008 Marcin Wichary http://www.flickr.com/photos/mwichary/2827326852/ © 2006 Marvin (PA) http://www.flickr.com/photos/mscolly/145052885/ 77 © 2010 Howard Lewis Ship
  • 78.
    Image Credits © 2007 John Kannenberg http://www.flickr.com/photos/jkannenberg/541057337/ © 2006 scott ogilvie http://www.flickr.com/photos/scottog/100582274/ © 2008 Ariel H. http://www.flickr.com/photos/fotosrotas/2730733412/ © 2006 John Ryan Brubaker http://www.flickr.com/photos/subconscience/297682093/ 78 © 2010 Howard Lewis Ship