Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)

13,712 views

Published on

As presented at What's Next? Conference, 26 May 2011, in Paris. http://www.whatsnextparis.com/

Published in: Technology
0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
13,712
On SlideShare
0
From Embeds
0
Number of Embeds
2,264
Actions
Shares
0
Downloads
154
Comments
0
Likes
12
Embeds 0
No embeds

No notes for slide

Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)

  1. Clojure: TowardsThe Essence OfProgrammingHoward M. Lewis Ship © 2011 Howard M. Lewis Ship
  2. essencenounthe intrinsic nature or indispensable quality ofsomething, esp. something abstract, thatdetermines its character : conflict is the essence ofdrama. © 2011 Howard M. Lewis Ship
  3. Applications Frameworks Libraries LanguageOperating System © 2011 Howard M. Lewis Ship
  4. Your code © 2011 Howard M. Lewis Ship
  5. CeremonyVs. Essence © 2011 Howard M. Lewis Ship
  6. Is your language ... © 2011 Howard M. Lewis Ship
  7. … holding you back? © 2011 Howard M. Lewis Ship
  8. 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: 200public static void sortByLastTrade(List<Stock> portfolio) { Comparator<Stock> c = new Comparator<Stock>() { public int compare(Stock o1, Stock o2) { return o1.getLastTrade() - o2.getLastTrade(); } }; Collections.sort(portfolio, c);} public static void sortByOpen(List<Stock> portfolio) { Comparator<Stock> c = new Comparator<Stock>() { public int compare(Stock o1, Stock o2) { return o1.getOpen() - o2.getOpen(); } }; Collections.sort(portfolio, c); } © 2011 Howard M. Lewis Ship
  9. :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=> (defn value-at-open [stock] …) #user/value-at-open user=> (sort-by value-at-open portfolio) ({:ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200} {:ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100}) © 2011 Howard M. Lewis Ship
  10. Clojure: TheLanguage © 2011 Howard M. Lewis Ship
  11. Aargh!(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 response)] (binding [*out* writer] (print-json json-value)))) true) © 2011 Howard M. Lewis Ship
  12. (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)] Panic Ensues (.setContentType response "application/json") (with-open [writer (.getWriter response)] (binding [*out* writer] (print-json json-value)))) true) Ouch! © 2011 Howard M. Lewis Ship
  13. © 2011 Howard M. Lewis Ship
  14. 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 © 2011 Howard M. Lewis Ship
  15. public static boolean renderJSON(Environment env, JSONObject object) { HttpServletResponse response = env.getServletAPI().getResponse(); response.setContentType("application/json"); Writer writer = null; try { writer = response.getWriter(); printJSON(writer, object); } finally { if (writer != null) { writer.close(); } } return true;} © 2011 Howard M. Lewis Ship
  16. (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 response)] (binding [*out* writer] (print-json json-value)))) 24/234 true)public static boolean renderJSON(Environment env, JSONObject object) { HttpServletResponse response = env.getServletAPI().getResponse(); response.setContentType("application/json"); Writer writer = null; try { writer = response.getWriter(); printJSON(writer, object); } finally { if (writer != null) { writer.close(); } } return true; 24/375} © 2011 Howard M. Lewis Ship
  17. public static float toFahrenheit(float celcius) { return 9 * celcius / 5 + 32; } High Low Precedence Precedence + (+ / 32 (/ (* 9 c) 5) * 5 32)9 celcius © 2011 Howard M. Lewis Ship
  18. public static float toFahrenheit(float celcius) { return 9 * celcius / 5 + 32;} $ hexdump bin/org/example/Conversions.class 0000000 ca fe ba be 00 00 00 31 00 17 07 00 02 01 00 17 public static toFahrenheit(F)F 0000010 6f 72 67 2f 65 78 61 6d 70 6c 65 2f 43 6f 6e 76 L0 0000020 65 72 73 69 6f 6e 73 07 00 04 01 00 0000030 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 10 74 6a 01 61 00 76 06 LINENUMBER 5 L0 0000040 3c 69 6e 69 74 3e 01 00 03 28 29 56 01 00 04 43 0000050 6f 64 65 0a 00 03 00 09 0c 00 05 00 06 01 00 0f LDC 9.0 0000060 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 0000070 00 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 FLOAD 0 0000080 61 62 6c 65 01 00 04 74 68 69 73 01 00 19 4c 6f FMUL 0000090 72 67 2f 65 78 61 6d 70 6c 65 2f 43 6f 6e 76 65 00000a0 72 73 69 6f 6e 73 3b 01 00 0c 74 6f 46 61 68 72 LDC 5.0 00000b0 65 6e 68 65 69 74 01 00 04 28 46 29 46 04 41 10 00000c0 00 00 04 40 a0 00 00 04 42 00 00 00 01 00 07 63 FDIV 00000d0 65 6c 63 69 75 73 01 00 01 46 01 00 0a 53 6f 75 LDC 32.0 00000e0 72 63 65 46 69 6c 65 01 00 10 43 6f 00000f0 73 69 6f 6e 73 2e 6a 61 76 61 00 21 6e 00 76 01 65 00 72 03 FADD 0000100 00 00 00 00 00 02 00 01 00 05 00 06 00 01 00 07 0000110 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00 08 FRETURN 0000120 b1 00 00 00 02 00 0a 00 00 00 06 00 01 00 00 00 0000130 03 00 0b 00 00 00 0c 00 01 00 00 00 05 00 0c 00 L1 0000140 0d 00 00 00 09 00 0e 00 0f 00 01 00 07 00 00 00 LOCALVARIABLE celcius F L0 L1 0 0000150 35 00 02 00 01 00 00 00 0b 12 10 22 6a 12 11 6e 0000160 12 12 62 ae 00 00 00 02 00 0a 00 00 00 06 00 01 MAXSTACK = 2 0000170 00 00 00 05 00 0b 00 00 00 0c 00 01 00 00 00 0b 0000180 00 13 00 14 00 00 00 01 00 15 00 00 00 02 00 16 MAXLOCALS = 1 0000190 © 2011 Howard M. Lewis Ship
  19. Clojure: Form IsStructure © 2011 Howard M. Lewis Ship
  20. (defn to-fahrenheit [celcius] (+ (/ (* 9 celcius) 5) 32)) © 2011 Howard M. Lewis Ship
  21. (1 2 "three") List of values(biggest 5 42) Function call(defn biggest "Find the maximum of two numbers" Function [x y] definition (if (> x y) x y)) © 2011 Howard M. Lewis Ship
  22. 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 © 2011 Howard M. Lewis Ship
  23. Source CodeRepl Input Clojure User Classes Java Evaluator Compiler ClojureSource Files Java Libraries JVM Operating System 23 © 2011 Howard M. Lewis Ship
  24. Simple Literals• Strings• Characters user=> "A Clojure String" "A Clojure String" user=> space• nil space user=> A• true A user=> nil nil is Java null• false nil user=> true true• integers user=> false false• floating point numbers user=> 0 0• ratios user=> 2.5 2.5 user=> 22/7 22/7 © 2011 Howard M. Lewis Ship
  25. Keywords• Literal values• Singleton instances• Especially useful as map keys user=> (identical? :a-keyword :a-keyword) true user=> (str "a" "-" "string") "a-string" user=> (identical? "a-string" (str "a" "-" "string")) false user=> (keyword "a-keyword") :a-keyword user=> (identical? :a-keyword (keyword "a-keyword")) true © 2011 Howard M. Lewis Ship
  26. Lists lst user=> (def lst (1 2 3)) #=(var user/lst) 1 user=> lst (1 2 3) user=> (first lst) 2 1 user=> (rest lst) (2 3) 3 © 2011 Howard M. Lewis Ship
  27. Lists 4 lst conjoin: Add element to list 1 user=> (conj lst 4) (4 1 2 3) user=> (cons 4 lst) 2 (4 1 2 3) 3 construct: new seq with new first element © 2011 Howard M. Lewis Ship
  28. Vectors user=> (def v [:moe :larry :curly]) #=(var user/v) user=> v [:moe :larry :curly] first, rest and others user=> (first v) work on lists, vectors or :moe any seq 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] © 2011 Howard M. Lewis Ship
  29. 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" Keywords act as a function user=> (:first-name m) "Howard" that takes a map 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 © 2011 Howard M. Lewis Ship
  30. 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"} © 2011 Howard M. Lewis Ship
  31. FunctionalProgramming © 2011 Howard M. Lewis Ship
  32. My First Program 10 X = 1 20 PRINT X "No it doesnt" -- Miss Dimascio 30 X = X + 1 40 GOTO 20 © 2011 Howard M. Lewis Ship
  33. Immutability © 2011 Howard M. Lewis Ship
  34. function |ˈfə ng k sh ən|nounA function, in a mathematical sense, expressesthe idea that one quantity (the argument of thefunction, also known as the input) completelydetermines another quantity (the value, orthe output). © 2011 Howard M. Lewis Ship
  35. Predictable © 2011 Howard M. Lewis Ship
  36. IsolationFrom Time © 2011 Howard M. Lewis Ship
  37. Inline anoymous Java Interop function (filter (fn [name] (not (.startsWith name "."))) names) #(…) anonymous function (filter #(not (.startsWith % ".")) names) % anonymous parameteruser=> (def names ["fred" "barney" ".hidden" "wilma"])#=(var user/names)user=> (filter #(not (.startsWith % ".")) names)("fred" "barney" "wilma")user=> (remove #(.startsWith % ".") names)("fred" "barney" "wilma") © 2011 Howard M. Lewis Ship
  38. Function as parameter(filter #(not (.startsWith % ".")) names) (defn require-extension [ext] (fn [file-name] (= ext (last (split-string file-name "."))))) Function as return value ❝Closure Oriented(filter (require-extension "gz") names) Programming❞ Composing functions © 2011 Howard M. Lewis Ship
  39. Java: Imperative Steps © 2011 Howard M. Lewis Ship
  40. 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: 200public static List<Double> getOpens(List<Stock> portfolio) { List<Double> result = new ArrayList<Double>(); for (Stock stock : portfolio) { result.add(stock.getOpen()); } return result;} © 2011 Howard M. Lewis Ship
  41. Clojure: Flow ofTransformations © 2011 Howard M. Lewis Ship
  42. :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 200user=> 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=> (map :open portfolio)(204.50M 29.08M 21.83M) © 2011 Howard M. Lewis Ship
  43. Laziness Is a Virtue © 2011 Howard M. Lewis Ship
  44. 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) © 2011 Howard M. Lewis Ship
  45. © 2011 Howard M. Lewis Ship
  46. La f zyuser=> (defn last-trade-value [stock] (* (:last-trade stock) (:shares stock)))#user/last-trade-valueuser=> (map last-trade-value portfolio)(20325.00M 1456.00M 4380.00M)user=> (map #(assoc % :last-trade-value (last-trade-value %)) portfolio)({:last-trade-value 20325.00M, :ticker "AAPL", :last-trade 203.25M, :open 204.50M, :shares 100} {:last-trade-value 1456.00M, :ticker "MSFT", :last-trade 29.12M, :open 29.08M, :shares 50} {:last-trade-value 4380.00M, :ticker "ORCL", :last-trade 21.90M, :open 21.83M, :shares 200}) © 2011 Howard M. Lewis Ship
  47. fuser=> (map last-trade-value portfolio)(20325.00M 1456.00M 4380.00M)user=> (reduce + (map last-trade-value portfolio))26161.00Muser=> (reduce + 0 [])0user=> (reduce + nil)0 Initial Value © 2011 Howard M. Lewis Ship
  48. fuser=> (def input-string "Clojure is a fascinating language with uniquecapabilities and total integration with Java.")#user/input-stringuser=> (seq input-string)(C l o j u r e space i s space a space f a s c i n a ti n g space l a n g u a g e space w i t h space u n i qu e space c a p a b i l i t i e s space a n d space t ot a l space i n t e g r a t i o n space w i t h space Ja v a .)user=> (reduce (fn [m k] (update-in m [k] #(inc (or % 0)))) {} (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} © 2011 Howard M. Lewis Ship
  49. List Comprehension La zy user=> (range 0 4) (0 1 2 3) 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]) © 2011 Howard M. Lewis Ship
  50. ❝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 © 2011 Howard M. Lewis Ship
  51. LanguageOwnership © 2011 Howard M. Lewis Ship
  52. Who Owns The JavaLanguage? Brian Goetz Mark Reinhold © 2011 Howard M. Lewis Ship
  53. Not You © 2011 Howard M. Lewis Ship
  54. Source CodeRepl Input Clojure User Classes Java Evaluator Compiler ClojureSource Files Java Libraries JVM Operating System © 2011 Howard M. Lewis Ship
  55. Who Owns Clojure? © 2011 Howard M. Lewis Ship
  56. Clojure In Clojure © 2011 Howard M. Lewis Ship
  57. Short CircuitingEvaluation if (person.isPharaoh() && Dead Pharoahs person.isDead() && buildPyramid(person)) { get a Pyramid person.entomb(); } boolean isPharoah = person.isPharoah(); boolean isDead = person.isDead(); boolean pyramidComplete = buildPyramid(person); Everyone gets a Pyramid! if (isPharoah && isDead && pyramidComplete) { person.entomb(); } © 2011 Howard M. Lewis Ship
  58. (if Function invocation: (all-true evaluate all parameters (.isPharaoh person) first (.isDead person) (build-pyramid person)) (.entomb person))(defn all-true ([] true) ([x] x) ([x & more] (if x (apply all-true more) x)))public static boolean allTrue(boolean... inputs) { for (boolean input : inputs) { if (!input) return false; } return true;} Java version of all-true © 2011 Howard M. Lewis Ship
  59. (if and short-circuits, so (and its not a function (.isPharaoh person) (.isDead person) (build-pyramid person)) (.entomb person)) And what exactly is if ?user=> (doc and)-------------------------clojure.core/and([] [x] [x & next])Macro Evaluates exprs one at a time, from left to right. If a form returns logical false (nil or false), and returns that value and doesnt evaluate any of the other expressions, otherwise it returns the value of the last expr. (and) returns true.nil © 2011 Howard M. Lewis Ship
  60. Caution: HeadExploding Zone © 2011 Howard M. Lewis Ship
  61. Forms Vectors Lists Maps SetsLiterals Function […] (1 2 3) {…} #{ … } Calls,"Hello" Special 2.5 Forms, nil Macros (a b c) © 2011 Howard M. Lewis Ship
  62. If: Special Form user=> (doc if) ------------------------- if Special Form Please see http://clojure.org/special_forms#if nil (if test then else?) Evaluates test. If not the singular values nil or false, evaluates and yields then, otherwise, evaluates and yields else. If else is not supplied it defaults to nil. … © 2011 Howard M. Lewis Ship
  63. Clojure Macros Reader Macro Evaluator Expansion Bytecode Generation © 2011 Howard M. Lewis Ship
  64. (if (if (and (if (.isPharaoh person) (.isPharaoh person) (.isDead person) Macro Expansion (if (.isDead person) (if (build-pyramid person) (build-pyramid person)) (.entomb person))))) (.entomb person)) Approximate expansion of macro © 2011 Howard M. Lewis Ship
  65. (defmacro and   ([] true)   ([x] x)   ([x & next]    `(let [and# ~x]       (if and# (and ~@next) and#)))) Evaluate (.isPharaoh person) only once (if (let [and_4422_auto (.isPharaoh person)](if (and Macro Expansion (if and_4422_auto (and (.isPharaoh person) (.isDead person) (.isDead person) (build-pyramid person)) (build-pyramid person)) and_4422_auto)) (.entomb person)) (.entomb person)) © 2011 Howard M. Lewis Ship
  66. Code Forms Macro Expansiondef if let fn . … Bytecode Generation © 2011 Howard M. Lewis Ship
  67. Boilerplate 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*"))))) © 2011 Howard M. Lewis Ship
  68. Domain Specific Languages(defview root-index [env] :html [ :head [ :title [ "Cascade Blog" ] ] :body [ :h1 [ "Cascade Blog" ] :ul { :class "recent-postings" } [ (template-for [posting (recent-postings env)] :li [ (render-link env show-posting (posting :id) (posting :title)) ]) ] :html ] ]) :head :body :title :h1 :ul "Cascade Blog" "Cascade Blog" (template-for …) © 2011 Howard M. Lewis Ship
  69. (defn list-items [coll] (template (format "%d items" (count coll)) :ul {:class :item-list} [ (template-for [item coll] :li [item]))) language from within executable code Extend Clojure Expand simple placeholder to 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))))))) © 2011 Howard M. Lewis Ship
  70. Wrap Up © 2011 Howard M. Lewis Ship
  71. essencenounthe intrinsic nature or indispensable quality ofsomething, esp. something abstract, thatdetermines its character : conflict is the essence ofdrama. © 2011 Howard M. Lewis Ship
  72. ❝Controlling complexity is the essence of computer programming❞Brian Kernigan © 2011 Howard M. Lewis Ship
  73. Control is the Essence ofProgramming © 2011 Howard M. Lewis Ship
  74. Evaluation © 2011 Howard M. Lewis Ship
  75. Language or Language Toolkit? © 2011 Howard M. Lewis Ship
  76. © 2011 Howard M. Lewis Ship
  77. Clojure• 1.2 release: 19 Aug 2010• Simple, regular syntax• Improves on Lisp: vectors, maps, sets• Fully integrates with Java http://www.clojure.org• Impressive functional & concurrency support• Most features not covered here © 2011 Howard M. Lewis Ship
  78. © 2011 Howard M. Lewis Ship
  79. http://java.ociweb.com/mark/clojure/article.html © 2011 Howard M. Lewis Ship
  80. http://tapestryjava.blogspot.com © 2011 Howard M. Lewis Ship
  81. © 2011 Howard M. Lewis Ship
  82. © 2011 Howard M. Lewis Ship
  83. Image Credits © 2007 John Kannenberg http://www.flickr.com/photos/jkannenberg/541057337/ © 2008 Jonathan Ziapour http://www.flickr.com/photos/jonathanziapour/2613204502/ © 2008 Alexandre Pizzera http://www.flickr.com/photos/alecss/2563917055 © 2006 scott ogilvie http://www.flickr.com/photos/scottog/100582274/ © 2008 Ariel H. http://www.flickr.com/photos/fotosrotas/2730733412/ © 2008 Howard M. Lewis Ship http://www.flickr.com/photos/hlship/3108306576 © 2010 yuichi.sakuraba http://www.flickr.com/photos/skrb/5107280055 © 2007 Jon Fife http://flickr.com/photos/good-karma/577632972/ © 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/ © 2011 Howard M. Lewis Ship
  84. Image Credits © 2009 Howard M. Lewis Ship http://www.flickr.com/photos/hlship/3603090614/ © 2006 John Ryan Brubaker http://www.flickr.com/photos/subconscience/297682093/ © 2003 A. Lipson http://www.andrewlipson.com/escher/relativity.html © 2007 Alan Chia http://flickr.com/photos/seven13avenue/2080281038/ © 2003 Randall Munroe http://xkcd.com/224/ © 2011 Howard M. Lewis Ship

×