Clojure Deep Dive

4,347 views
4,209 views

Published on

A deep dive into the core language features and functions of Clojure, as well as a discussion of how Clojure compiles to Java bytecode, and a few of Clojure's pitfalls.

Published in: Technology, Sports
1 Comment
30 Likes
Statistics
Notes
  • I wish there were links to the videos of these talks as well, that would be awesome!!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
4,347
On SlideShare
0
From Embeds
0
Number of Embeds
28
Actions
Shares
0
Downloads
0
Comments
1
Likes
30
Embeds 0
No embeds

No notes for slide

Clojure Deep Dive

  1. 1. Clojure Deep Dive Howard M. Lewis Ship TWD Consulting hlship@gmail.com 1 © 2009 Howard Lewis Ship
  2. 2. Agenda • Core Language • Standard Tools • Clojure Compilation • Clojure Pitfalls 2 © 2009 Howard Lewis Ship
  3. 3. Core Language 3 © 2009 Howard Lewis Ship
  4. 4. What's A Form? Constants: Keywords: Symbols: 42 :foo map "hello" :tag strutils2/take Data Structures: […] {…} #{ … } Callables: Special Forms: Function Call: Macros Expansion: (if …) (map …) (and …) (var …) (or …) (do …) (ns …) 4 © 2009 Howard Lewis Ship
  5. 5. defn: Define a function Symbol (in current namespace) Optional doc string (defn to-seq "Converts the object to a sequence (a vector) unless it is already sequential." [obj] (if (sequential? obj) obj [obj])) List of parameters user=> (doc to-seq) ------------------------- user/to-seq ([obj]) Converts the object to a sequence (a vector) unless it is already sequential. nil user=> 5 © 2009 Howard Lewis Ship
  6. 6. defn: Multiple Arities One doc string Series of argument lists / forms (defn not= "Same as (not (= obj1 obj2))" ([x] false) ([x y] (not (= x y))) ([x y & more] (not (apply = x y more)))) & symbol ➠ provide remaining values as a seq user=> (doc not=) ------------------------- clojure.core/not= ([x] [x y] [x y & more]) Same as (not (= obj1 obj2)) nil user=> 6 © 2009 Howard Lewis Ship
  7. 7. let: local symbols (let [center (calc-center-for-frame frame) x (get-x center) y (get-y center)] …) •Even number: symbol1 form1 symbol2 form2 … •Not variables: symbols are read-only •Can re-define symbols 7 © 2009 Howard Lewis Ship
  8. 8. do: evaluate for side-effects (if value-found (do (println "Found the value.") value-found)) Last form is the result ; Broken (if value-found (println "Found the value.") If true, side effect & return nil value-found) If false, return value (false or nil) 8 © 2009 Howard Lewis Ship
  9. 9. Branching: if, when (if test then-form) (if test then-form else-form) • Special form: not a function • Evaluates test, then evaluates & returns either then-form or else-form • false and nil ➠ logical false • anything else ➠ logical true (when test then-forms) (if-not test then-form) (if-not test then-form else-form) Any number of forms in an implicit do Like (if (not test) then else) (when-not test then-forms) 9 © 2009 Howard Lewis Ship
  10. 10. if-let, when-let (if-let [symbol test] then-form) (if-let [symbol test] then-form else-form) Symbol can be referenced (when-let [symbol test] then-forms) Any number of forms in an implicit do 10 © 2009 Howard Lewis Ship
  11. 11. or, and (or form…) (and form…) • or: first value from list that evaluates to true • Will return value of last expression (false or nil) • (or) returns nil • and: first logical false (nil or false) • Returns value of last expression (if prior are logical true) • (and) return true • Can be used as a "guard" Only invoke symbol and find-ns (defn dispatch-named-function-to-pipeline if ns-name is non-nil [env pipeline] (let [split-path (-> env :cascade :split-path) [_ ns-name fn-name] split-path fn-namespace (and ns-name (find-ns (symbol ns-name))) named-function (and fn-namespace fn-name (ns-resolve fn-namespace (symbol fn-name))) new-env (assoc-in env [:cascade :extra-path] (drop 3 split-path))] (call-pipeline pipeline new-env named-function))) 11 © 2009 Howard Lewis Ship
  12. 12. Destructuring Bu N wo sy o rk • Automatically extract values from data structures ! • Binding forms: • Map to extract keys • Vector to extract indexed values • Function parameters One un-named parameter • let, if-let, when-let, etc. destructured to two symbols user=> (defn natural-name [{fname :first-name lname :last-name}] (str lname ", " fname)) #'user/natural-name user=> (natural-name (persons 1)) "Simon, Scott" user=> 12 © 2009 Howard Lewis Ship
  13. 13. Destructuring • Binding Forms: • symbol (let [x (get-x center)] … ) • vector of binding forms (let [[x y] center] … ) • map of binding form to map key (let [{fname :first-name lname :last-name} person] …) 13 © 2009 Howard Lewis Ship
  14. 14. Vector Destructuring center must be sequential (let [[x y] center] …) May be nil if not enough values in center (let [[first-born second-born & other-children] children] …) & symbol gets the remaining values (let [[first-born second-born & other-children :as children] (find-children parent)] …) Nested binding forms! :as symbol gets the full list user=> (let [[[x1 y1][x2 y2]] [[1 2] [3 4]]] [x1 y1 x2 y2]) [1 2 3 4] user=> 14 © 2009 Howard Lewis Ship
  15. 15. Map Destructuring (defn natural-name [{fname :first-name lname :last-name}] (str lname ", " fname)) (defn natural-name [{:keys [first-name last-name]}] (str last-name ", " first-name)) :keys maps a keyword key for each symbol :or sets defaults for missing keys user=> (defn natural-name [{:keys [first-name last-name] :or {first-name "(none)" last-name "(none)"}}] (str last-name ", " first-name)) #'user/natural-name user=> (natural-name {:first-name "Gromit"}) "(none), Gromit" user=> (natural-name {:first-name "Gromit" :last-name nil}) ", Gromit" user=> :as symbol to get the original map :strs ➠ like :keys, but maps to string keys, not keyword keys :syms ➠ like :keys, but maps to symbol keys, not keyword keys 15 © 2009 Howard Lewis Ship
  16. 16. ns: define namespaces (ns example.utils) cascade/dispatcher.clj (ns cascade.dispatcher (:import (javax.servlet ServletResponse)) File: example/utils.clj (:use (cascade config dom logging path-map pipeline) (cascade.internal utils))) (ns namespace (:import …) Optional directives (:use …)) user=> (defn natural-name [{fname :first-name lname :last-name}] (str lname ", " fname)) #'user/natural-name user=> (natural-name (persons 1)) "Simon, Scott" user=> user/natural-name Namespace: user Symbol: natural-name 16 © 2009 Howard Lewis Ship
  17. 17. :import Java Classes Package name, then any number of classes/interfaces (ns cascade.dispatcher (:import (javax.servlet ServletResponse)) …) Repeat for other packages Can also list fully qualify class names 17 © 2009 Howard Lewis Ship
  18. 18. :use Import Namespaces (:use clojure.contrib.pprint) Symbols defined by pprint are available, i.e. (pprint [1 2 3]) (:use (clojure.contrib pprint monads)) Add multiple namespaces under the root namespace (:use (clojure.contrib (str-utils2 :only [join map-str])) Uses just join and map-str from str-utils2 (:use (clojure.contrib (str-utils2 :exclude [take replace drop])) Exclude just some of the symbols (:use (clojure.contrib (str-utils2 :rename {take strtake})) Use str-utils2, mapping take as strtake 18 © 2009 Howard Lewis Ship
  19. 19. Other ns directives • :require ➠ load a namespace but don't import it • :gen-class ➠ create a Java class from functions in the namespace • :refer-clojure ➠ import selected clojure.core symbols • :load ➠ load Clojure scripts (not namespaces) 19 © 2009 Howard Lewis Ship
  20. 20. Standard Tools 20 © 2009 Howard Lewis Ship
  21. 21. Anonymous Functions #() ➠ implicit anonymous function (filter #(not (.startsWith % ".")) names) % is replaced by the function's parameter %n is parameter #n (map #(assoc %1 :sort-index %2) (sort-by :age persons) (iterate inc 0)) inline anonymous function (map 21 (fn [person-map sort-index] (assoc person-map :sort-index sort-index)) (sort-by :age persons) (iterate inc 0)) © 2009 Howard Lewis Ship
  22. 22. What are seqs? Seqable seq() : ISeq IPersistentCollection Sequential count() : int cons(Object): IPersistentCollection empty() : IPersistentCollection equiv(Object) : boolean PersistentSet ISeq first() : Object next(): ISeq more(): ISeq cons(Object): ISeq Returned from map, for, filter, remove, etc. PersistentList PersistentVector LazySeq 22 © 2009 Howard Lewis Ship
  23. 23. assoc-in / update-in user=> (def person { :first-name "Howard" :last-name "Lewis Ship" :address { :street "123 NW 12th Ave. #541" :city "Portland" :state "OR" :zip 97309 }}) #'user/person user=> (assoc-in person [:address :street] "2647 SE 97th Ave.") {:first-name "Howard", :last-name "Lewis Ship", :address {:street "2647 SE 97th Ave.", :city "Portland", :state "OR", :zip 97309}} user=> (update-in person [:address :zip] + 5) {:first-name "Howard", :last-name "Lewis Ship", :address {:street "123 NW 12th Ave. #541", :city "Portland", :state "OR", :zip 97314}} user=> :first-name "Howard" :last-name "Lewis Ship" :address :street "123 NW 12th Ave." :city "Portland" :state "OR" :zip 97309 (update-in person [:address :zip] + 5) (+ 97309 5) :first-name "Howard" :last-name "Lewis Ship" :address :street "123 NW 12th Ave." :city "Portland" :state "OR" :zip 97314 23 © 2009 Howard Lewis Ship
  24. 24. map La zy f :age acts as a function user=> (map :age persons) (42 44 29) user=> (map #(apply str (reverse (% :first-name))) persons) ("drawoH" "ttocS" "ylloM") user=> (map identity (persons 0)) ([:first-name "Howard"] iterate a map ➠ key/value pairs [:last-name "Lewis Ship"] [:age 42]) user=> (seq (persons 0)) ([:first-name "Howard"] [:last-name "Lewis Ship"] [:age 42]) user=> 24 © 2009 Howard Lewis Ship
  25. 25. map La zy f N seqs ➠ N parameters user=> (map #(assoc %1 :sort-index %2) (sort-by :age persons) (iterate inc 0)) ({:sort-index 0, :first-name "Molly", :last-name "Newman", :age 29} {:sort-index 1, :first-name "Howard", :last-name "Lewis Ship", :age 42} {:sort-index 2, :first-name "Scott", :last-name "Simon", :age 44}) user=> 25 © 2009 Howard Lewis Ship
  26. 26. reduce f user=> (map :age persons) (42 44 29 38) user=> (reduce + (map :age persons)) 153 (reduce + (map :age persons)) 26 © 2009 Howard Lewis Ship
  27. 27. reduce f user=> (map :age persons) (42 44 29 38) user=> (reduce + (map :age persons)) 153 (reduce + (map :age persons)) (reduce + '(42 44 29 38)) 27 © 2009 Howard Lewis Ship
  28. 28. reduce f user=> (map :age persons) (42 44 29 38) user=> (reduce + (map :age persons)) 153 (reduce + (map :age persons)) (reduce + '(42 44 29 38)) (+ (+ (+ 42 44) 29) 38) 28 © 2009 Howard Lewis Ship
  29. 29. reduce f user=> (map :age persons) (42 44 29 38) user=> (reduce + (map :age persons)) 153 (reduce + (map :age persons)) (reduce + '(42 44 29 38)) (+ (+ (+ 42 44) 29) 38) (+ (+ 86 29) 38) 29 © 2009 Howard Lewis Ship
  30. 30. reduce f user=> (map :age persons) (42 44 29 38) user=> (reduce + (map :age persons)) 153 (reduce + (map :age persons)) (reduce + '(42 44 29 38)) (+ (+ (+ 42 44) 29) 38) (+ (+ 86 29) 38) (+ 115 38) 30 © 2009 Howard Lewis Ship
  31. 31. reduce f user=> (map :age persons) (42 44 29 38) user=> (reduce + (map :age persons)) 153 (reduce + (map :age persons)) (reduce + '(42 44 29 38)) (+ (+ (+ 42 44) 29) 38) (+ (+ 86 29) 38) (+ 115 38) 153 31 © 2009 Howard Lewis Ship
  32. 32. 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] #(if (nil? %) 1 (inc %)))) 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=> 32 © 2009 Howard Lewis Ship
  33. 33. filter / remove La zy f ? user=> (remove #(< (% :age) 30) persons) ({:first-name "Howard", :last-name "Lewis Ship", :age 42} {:first-name "Scott", :last-name "Simon", :age 44}) user=> (filter #(< (% :age) 30) persons) ({:first-name "Molly", :last-name "Newman", :age 29}) user=> 33 © 2009 Howard Lewis Ship
  34. 34. apply f user=> (map :age persons) (42 44 29) user=> (apply + (map :age persons)) 115 user=> (apply + (map :age persons)) (apply + '(42 44 29)) (+ 42 44 29) 115 34 © 2009 Howard Lewis Ship
  35. 35. Clojure Compilation 35 © 2009 Howard Lewis Ship
  36. 36. Function Calls (remove nil? coll) IFn invoke() : Object invoke(Object) : Object Namespace invoke(Object, Object) : Object … applyTo(ISeq) : Object Var get() clojure.core$remove__4782 PersistentMap Keyword 36 © 2009 Howard Lewis Ship
  37. 37. Compiled to Bytecode (defn first-non-nil "Returns the first non-nil value from the collection." [coll] (first (remove nil? coll))) package com.howardlewisship.cascade.internal; import clojure.lang.*; public class utils$first_non_nil__24 extends AFunction { public Object invoke(Object coll) throws Exception { return ((IFn)const__0.get()).invoke(((IFn)const__1.get()). invoke(const__2.get(), coll = null)); } public static final Var const__0 = (Var)RT.var("clojure.core", "first"); public static final Var const__1 = (Var)RT.var("clojure.core", "remove"); public static final Var const__2 = (Var)RT.var("clojure.core", "nil?"); public utils$first_non_nil__24() { } } 37 © 2009 Howard Lewis Ship
  38. 38. Compiled to Bytecode (defn first-non-nil "Returns the first non-nil value from the collection." [coll] (first (remove nil? coll))) package com.howardlewisship.cascade.internal; import clojure.lang.*; public class utils$first_non_nil__24 extends AFunction { public Object invoke(Object coll) throws Exception { return ((IFn)const__0.get()).invoke(((IFn)const__1.get()). invoke(const__2.get(), coll = null)); } public static final Var const__0 = (Var)RT.var("clojure.core", "first"); public static final Var const__1 = (Var)RT.var("clojure.core", "remove"); public static final Var const__2 = (Var)RT.var("clojure.core", "nil?"); public utils$first_non_nil__24() { } } 38 © 2009 Howard Lewis Ship
  39. 39. Compiled to Bytecode (defn first-non-nil "Returns the first non-nil value from the collection." [coll] (first (remove nil? coll))) package com.howardlewisship.cascade.internal; import clojure.lang.*; public class utils$first_non_nil__24 extends AFunction { public Object invoke(Object coll) throws Exception { return ((IFn)const__0.get()).invoke(((IFn)const__1.get()). invoke(const__2.get(), coll = null)); } public static final Var const__0 = (Var)RT.var("clojure.core", "first"); public static final Var const__1 = (Var)RT.var("clojure.core", "remove"); public static final Var const__2 = (Var)RT.var("clojure.core", "nil?"); public utils$first_non_nil__24() { } } 39 © 2009 Howard Lewis Ship
  40. 40. Compiled to Bytecode (defn first-non-nil "Returns the first non-nil value from the collection." [coll] (first (remove nil? coll))) package com.howardlewisship.cascade.internal; import clojure.lang.*; public class utils$first_non_nil__24 extends AFunction { public Object invoke(Object coll) throws Exception { return ((IFn)const__0.get()).invoke(((IFn)const__1.get()). invoke(const__2.get(), coll = null)); } public static final Var const__0 = (Var)RT.var("clojure.core", "first"); public static final Var const__1 = (Var)RT.var("clojure.core", "remove"); public static final Var const__2 = (Var)RT.var("clojure.core", "nil?"); public utils$first_non_nil__24() { } } 40 © 2009 Howard Lewis Ship
  41. 41. Compiled to Bytecode (defn format-date [env params] (let [#^Date date (params :date) package app1; #^DateFormat fmt (DateFormat/getDateTimeInstance DateFormat/MEDIUM import clojure.lang.*; DateFormat/MEDIUM)] import java.text.DateFormat; (.format fmt date))) import java.util.Date; public class fragments$format_date__21 extends AFunction { public Object invoke(Object env, Object params) throws Exception { Object date = ((IFn)params).invoke(const__1); Object fmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); env = null; params = null; date = null; fmt = null; return ((DateFormat)fmt).format((Date)date); } public static final Var const__0 = (Var)RT.var("clojure.core", "let"); public static final Object const__1 = Keyword.intern(Symbol.create(null, "date")); public fragments$format_date__21() { } } 41 © 2009 Howard Lewis Ship
  42. 42. Compiled to Bytecode (defn format-date [env params] (let [#^Date date (params :date) package app1; #^DateFormat fmt (DateFormat/getDateTimeInstance DateFormat/MEDIUM import clojure.lang.*; DateFormat/MEDIUM)] import java.text.DateFormat; (.format fmt date))) import java.util.Date; public class fragments$format_date__21 extends AFunction { public Object invoke(Object env, Object params) throws Exception { Object date = ((IFn)params).invoke(const__1); Object fmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); env = null; params = null; date = null; fmt = null; return ((DateFormat)fmt).format((Date)date); } public static final Var const__0 = (Var)RT.var("clojure.core", "let"); public static final Object const__1 = Keyword.intern(Symbol.create(null, "date")); public fragments$format_date__21() { } } 42 © 2009 Howard Lewis Ship
  43. 43. Compiled to Bytecode (defn format-date [env params] (let [#^Date date (params :date) package app1; #^DateFormat fmt (DateFormat/getDateTimeInstance DateFormat/MEDIUM import clojure.lang.*; DateFormat/MEDIUM)] import java.text.DateFormat; (.format fmt date))) import java.util.Date; public class fragments$format_date__21 extends AFunction { public Object invoke(Object env, Object params) throws Exception { Object date = ((IFn)params).invoke(const__1); Object fmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); env = null; params = null; date = null; fmt = null; return ((DateFormat)fmt).format((Date)date); } public static final Var const__0 = (Var)RT.var("clojure.core", "let"); public static final Object const__1 = Keyword.intern(Symbol.create(null, "date")); public fragments$format_date__21() { } } 43 © 2009 Howard Lewis Ship
  44. 44. Compiled to Bytecode (defn format-date [env params] (let [#^Date date (params :date) package app1; #^DateFormat fmt (DateFormat/getDateTimeInstance DateFormat/MEDIUM import clojure.lang.*; DateFormat/MEDIUM)] import java.text.DateFormat; (.format fmt date))) import java.util.Date; public class fragments$format_date__21 extends AFunction { public Object invoke(Object env, Object params) throws Exception { Object date = ((IFn)params).invoke(const__1); Object fmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); env = null; params = null; date = null; fmt = null; return ((DateFormat)fmt).format((Date)date); } public static final Var const__0 = (Var)RT.var("clojure.core", "let"); public static final Object const__1 = Keyword.intern(Symbol.create(null, "date")); public fragments$format_date__21() { } } 44 © 2009 Howard Lewis Ship
  45. 45. Compiled to Bytecode (defn format-date [env params] (let [#^Date date (params :date) package app1; #^DateFormat fmt (DateFormat/getDateTimeInstance DateFormat/MEDIUM import clojure.lang.*; DateFormat/MEDIUM)] import java.text.DateFormat; (.format fmt date))) import java.util.Date; public class fragments$format_date__21 extends AFunction { public Object invoke(Object env, Object params) throws Exception { Object date = ((IFn)params).invoke(const__1); Object fmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); env = null; params = null; date = null; fmt = null; return ((DateFormat)fmt).format((Date)date); } public static final Var const__0 = (Var)RT.var("clojure.core", "let"); public static final Object const__1 = Keyword.intern(Symbol.create(null, "date")); public fragments$format_date__21() { } } 45 © 2009 Howard Lewis Ship
  46. 46. Ahead of Time Compilation app/fragments.clj (ns app.fragments (:import (java.util Date) (java.text DateFormat))) (defn format-date [env params] (…)) Compiler app/fragments__init.class app/fragments$format_date__21.class 46 © 2009 Howard Lewis Ship
  47. 47. Ahead Of Time Compilation build.xml <target name="compile" description="Compile Clojure sources."> <mkdir dir="${classes.dir}" /> <pathconvert pathsep=" " property="compile.namespaces"> <fileset dir="${src.dir}" includes="**/*.clj" /> <chainedmapper> <packagemapper from="${basedir}/${src.dir}/*.clj" to="*" /> <filtermapper> <replacestring from="_" to="-" /> </filtermapper> </chainedmapper> </pathconvert> <java classname="clojure.lang.Compile"> <classpath> <path refid="libs.path" /> <path location="${classes.dir}" /> <path location="${src.dir}" /> </classpath> <sysproperty key="clojure.compile.path" value="${classes.dir}" /> <arg line="${compile.namespaces}" /> </java> </target> 47 © 2009 Howard Lewis Ship
  48. 48. Ahead Of Time Compilation build.xml <target name="compile" description="Compile Clojure sources."> <mkdir dir="${classes.dir}" /> <pathconvert pathsep=" " property="compile.namespaces"> <fileset dir="${src.dir}" includes="**/*.clj" /> <chainedmapper> <packagemapper from="${basedir}/${src.dir}/*.clj" to="*" /> <filtermapper> <replacestring from="_" to="-" /> </filtermapper> Locate *.clj under </chainedmapper> ${src.dir} and convert to </pathconvert> namespace names <java classname="clojure.lang.Compile"> <classpath> <path refid="libs.path" /> <path location="${classes.dir}" /> <path location="${src.dir}" /> </classpath> <sysproperty key="clojure.compile.path" value="${classes.dir}" /> <arg line="${compile.namespaces}" /> </java> </target> 48 © 2009 Howard Lewis Ship
  49. 49. Ahead Of Time Compilation build.xml <target name="compile" description="Compile Clojure sources."> <mkdir dir="${classes.dir}" /> <pathconvert pathsep=" " property="compile.namespaces"> <fileset dir="${src.dir}" includes="**/*.clj" /> <chainedmapper> <packagemapper from="${basedir}/${src.dir}/*.clj" to="*" /> <filtermapper> <replacestring from="_" to="-" /> </filtermapper> </chainedmapper> </pathconvert> clojure.jar, source <java classname="clojure.lang.Compile"> directory and output <classpath> directory must be on <path refid="libs.path" /> classpath <path location="${classes.dir}" /> <path location="${src.dir}" /> </classpath> <sysproperty key="clojure.compile.path" value="${classes.dir}" /> <arg line="${compile.namespaces}" /> </java> </target> 49 © 2009 Howard Lewis Ship
  50. 50. Ahead Of Time Compilation build.xml <target name="compile" description="Compile Clojure sources."> <mkdir dir="${classes.dir}" /> <pathconvert pathsep=" " property="compile.namespaces"> <fileset dir="${src.dir}" includes="**/*.clj" /> <chainedmapper> <packagemapper from="${basedir}/${src.dir}/*.clj" to="*" /> <filtermapper> <replacestring from="_" to="-" /> </filtermapper> </chainedmapper> </pathconvert> Output directory for <java classname="clojure.lang.Compile"> generated classes <classpath> <path refid="libs.path" /> <path location="${classes.dir}" /> <path location="${src.dir}" /> </classpath> <sysproperty key="clojure.compile.path" value="${classes.dir}" /> <arg line="${compile.namespaces}" /> </java> </target> List of namespaces to compile 50 © 2009 Howard Lewis Ship
  51. 51. Clojure Pitfalls 51 © 2009 Howard Lewis Ship
  52. 52. IDE Support 52 © 2009 Howard Lewis Ship
  53. 53. Laziness RuntimeException: Divide by zero 53 © 2009 Howard Lewis Ship
  54. 54. Laziness Clojure 1.0.0- 1:1 user=> (def nums (map / (range 0 10) (reverse (range 0 10)))) #'user/nums 1:2 user=> nums java.lang.ArithmeticException: Divide by zero (0 1/8 2/7 1/2 4/5 5/4 2 7/2 1:3 user=> (.. *e getCause printStackTrace) java.lang.ArithmeticException: Divide by zero at clojure.lang.Numbers.divide(Numbers.java:138) at clojure.core$_SLASH___3350.invoke(core.clj:575) at clojure.core$map__3815$fn__3822.invoke(core.clj:1508) at clojure.lang.LazySeq.seq(LazySeq.java:41) at clojure.lang.Cons.next(Cons.java:37) at clojure.lang.RT.next(RT.java:560) at clojure.core$next__3117.invoke(core.clj:50) at clojure.core$nthnext__4405.invoke(core.clj:2531) at clojure.core$print_sequential__5354.invoke(core_print.clj:53) at clojure.core$fn__5439.invoke(core_print.clj:136) at clojure.lang.MultiFn.invoke(MultiFn.java:161) at clojure.core$pr_on__4145.invoke(core.clj:2020) at clojure.core$pr__4148.invoke(core.clj:2030) at clojure.lang.AFn.applyToHelper(AFn.java:173) at clojure.lang.RestFn.applyTo(RestFn.java:137) at clojure.core$apply__3243.doInvoke(core.clj:390) at clojure.lang.RestFn.invoke(RestFn.java:428) at clojure.core$prn__4159.doInvoke(core.clj:2053) at clojure.lang.RestFn.invoke(RestFn.java:413) at clojure.main$repl__5813$read_eval_print__5825.invoke(main.clj:177) at clojure.main$repl__5813.doInvoke(main.clj:193) at clojure.lang.RestFn.invoke(RestFn.java:876) at clojure.contrib.repl_ln$repl__84.doInvoke(repl_ln.clj:259) at clojure.lang.RestFn.invoke(RestFn.java:426) at clojure.contrib.repl_ln$_main__44.doInvoke(repl_ln.clj:136) at clojure.lang.RestFn.applyTo(RestFn.java:142) at clojure.contrib.repl_ln.main(Unknown Source) nil 1:7 user=> 54 © 2009 Howard Lewis Ship
  55. 55. Lack of Type Checking 55 © 2009 Howard Lewis Ship
  56. 56. Spot the Error view_manager.clj (defn to-dom-node-seq [any] "Converts the result of a render function to a seq as needed." (cond (nil? any) nil (sequential? any) any ; A map is assumed to be a DOM node, wrap it in a vector (map? any) [map] (string? any) [(struct-map dom-node :type :text :value any)] true (throw (RuntimeException. (format "A rendering function returned %s. ↵ Rendering functions should return nil, a string, a seq of DOM nodes, or a single ↵ DOM node." (pr-str any)))))) 56 © 2009 Howard Lewis Ship
  57. 57. Exception ERROR in (simple-view-and-fragment) (test_is.clj:657) Uncaught exception, not in assertion. expected: nil actual: java.lang.IllegalArgumentException: Wrong number of args passed to: core$map at clojure.lang.AFn.throwArity (AFn.java:449) clojure.lang.RestFn.invoke (RestFn.java:417) com.howardlewisship.cascade.dom$fn__960.doInvoke (dom.clj:58) clojure.lang.RestFn.invoke (RestFn.java:443) clojure.lang.MultiFn.invoke (MultiFn.java:165) com.howardlewisship.cascade.dom/render_xml_with_ns (dom.clj:118) com.howardlewisship.cascade.dom/render_xml (dom.clj:125) com.howardlewisship.cascade.test_views/render (test_views.clj:27) com.howardlewisship.cascade.test_views/test_view (test_views.clj:56) com.howardlewisship.cascade.test_views/test_view (test_views.clj:52) com.howardlewisship.cascade.test_views/fn (test_views.clj:62) dom.clj (defmulti render-node-xml (fn [node & rest] (node :type))) 57 © 2009 Howard Lewis Ship
  58. 58. Spot the Error view_manager.clj (defn to-dom-node-seq [any] "Converts the result of a render function to a seq as needed." (cond (nil? any) nil (sequential? any) any ; A map is assumed to be a DOM node, wrap it in a vector (map? any) [map] (string? any) [(struct-map dom-node :type :text :value any)] true (throw (RuntimeException. (format "A rendering function returned %s. ↵ Rendering functions should return nil, a string, a seq of DOM nodes, or a single ↵ DOM node." (pr-str any)))))) Returning function clojure.core/map; should return any (the parameter) 58 © 2009 Howard Lewis Ship
  59. 59. Bus Factor 59 © 2009 Howard Lewis Ship
  60. 60. GitHub Factor 60 © 2009 Howard Lewis Ship
  61. 61. Performance 61 © 2009 Howard Lewis Ship
  62. 62. Performance (defn shorts-to-bytes [#^shorts src #^bytes dst words]  (loop [src-offset (int 0)         dst-offset (int 0)]    (when (< src-offset words)      (let [sample (short (aget src src-offset))]        (aset-byte dst dst-offset (byte sample))        (aset-byte dst (inc dst-offset) (byte (bit-shift-right sample 8))))      (recur (inc src-offset) (unchecked-add 2 dst-offset)))))  public static void shortsToBytes(short[] src, byte[] dst, int len)  {   int idx = 0; 900 ms   short s;   while (len-- > 0) {     s = src[idx];     dst[idx*2] = (byte)s; 675 ms     dst[idx*2+1] = (byte)(s>>>8);     idx++;   }  } 450 ms 225 ms 0 ms Java Clojure 62 © 2009 Howard Lewis Ship
  63. 63. API Docs 63 © 2009 Howard Lewis Ship
  64. 64. Wrap Up 64 © 2009 Howard Lewis Ship
  65. 65. What's Not Covered? • multimethods ➠ inheritance-like function dispatch • Concurrency Support ➠ atoms, agents, refs & vars http://www.clojure.org • loop & recur ➠ iterative-style coding • clojure.contrib ➠ Community library • Macros & Meta-Programming ➠ Create the language you want inside Clojure • Tuning Clojure Performance • More …. 65 © 2009 Howard Lewis Ship
  66. 66. Stuart Halloway Pragmatic Bookshelf http://pragprog.com/titles/shcloj/programming-clojure 66 © 2009 Howard Lewis Ship
  67. 67. http://jnb.ociweb.com/jnb/jnbMar2009.html 67 © 2009 Howard Lewis Ship
  68. 68. Image Credits © 2005 Jean-Philippe Daigle http://www.flickr.com/photos/jpdaigle/59942231/ © 2007 Howard Gees http://www.flickr.com/photos/cyberslayer/952121271/ © 2006 Simon Law http://www.flickr.com/photos/sfllaw/222795669/ © 2008 II-cocoy22-II http://www.flickr.com/photos/30954876@N07/3090155264/ © 2009 Martin Biskoping http://www.flickr.com/photos/mbiskoping/3388639698/ © 2008 sea turtle http://www.flickr.com/photos/sea-turtle/3049443478/ © 2009 Tulio Bertorini http://www.flickr.com/photos/tuliobertorini/3159130251/ © 2007 Adam Balch http://www.flickr.com/photos/triplemaximus/795758146/ © 2008 jessamyn west http://www.flickr.com/photos/iamthebestartist/2636607001/ 68 © 2009 Howard Lewis Ship

×