CHANGE YOUR MINDSETFunktionales Programmieren mit Clojure chris_betz xing.to/betz
About me Promotion in KI - und ca. 10 Jahre Erfahrung mit Common LISP eCommerce/Finance mit SinnerSchrader - in Java Secur...
HERO11  Mobile Sports Entertainment  „Beat the coach“, live während  reale Fußballspiele laufen  iOS Client, Clojure Serve...
Warum Clojure?Start „auf der grünen Wiese“: Freie Wahl derSpracheScala? oder Clojure? oder Go?Zwei Entwickler „pro Clojure...
Was machen andere damit?Twitter: big data processing (war Backtype:Storm)Flightcaster: realtime flight delay predictionRun...
Was uns fasziniert...
SIMPLICITY
Reduktion aufs Wesentliche Java: class HelloWorldApp {   public static void main(String[] args) {     System.out.println("...
Erweitertes Beispiel, aus : Programming Clojure, Stuart HallowaySIMPLICITY
org.apache.commons.lang.StringUtils: indexOfAny - findet in einem String das erste Vorkommen eines Zeichens aus einer vorg...
public static int indexOfAny(String str, char[] searchChars) {   if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) {   ...
Wie macht man das in Clojure?SIMPLICITY
(defn indexed [coll] (map vector (iterate inc 0) coll)) (indexed "abcde")    ([0 a] [1 b] [2 c] [3 d] [4 e])SIMPLICITY
(defn index-filter [pred coll]   (when pred     (for [[idx elt] (indexed coll) :when (pred elt)] idx))) (index-filter #{a ...
(defn index-of-any [pred coll]   (first (index-filter pred coll))) (index-of-any #{z a} "zzabyycdxx")    0 (index-of-any #...
(defn indexed [coll]   (map vector (iterate inc 0) coll)) (defn index-filter [pred coll]   (when pred     (for [[idx elt] ...
Simplicity                            Exits/   Metrik     LOC Branches         Variables                           Method ...
weitere UnterschiedeSIMPLICITY
weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequencesSIMPLICITY
weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach ei...
weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach ei...
weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach ei...
weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach ei...
lazy sequences Natürlich werden in index-of-any nicht erst alle Treffer erzeugt und dann alle bis auf den ersten weggeworfe...
for: Sequence comprehension syntaktisches Konstrukt zum Erzeugen einer Liste aus existierenden Listen entlehnt aus mathema...
Unsere! Sprache
Die Sprache ist vollständig erweiterbar.Erweiterungen sind vom eigentlichenSprachkern nicht zu unterscheiden.UNSERE! Sprache
Beispiel aus dem Noir Webframework:(defpage „/person/:id.html“ [id]...)UNSERE! Sprache
Beispiel aus Monger:;; find scores 10 to 20(with-collection "scores"  (find {})  (fields ,,, [:score :name])  (sort ,,, {:...
Hintergrund der Erweiterbarkeit- Homoiconicity: Code is Data- Das mächtigste Makrosystem:  Clojure verwenden, um Clojure z...
Java-Interop
alle Java - Libraries stehen zur Verfügungviele wichtige Libs mit clj-wrapperninteressante Libs in Clojure:noir (Web Frame...
Aber:Man braucht einanderes Mindset
Funktional- was heisst    das?
Funktionen als First-class              ObjekteMaximum einer Collection von Zahlen?Wie würde man das in Java machen?Funkti...
Funktionen als First-class              ObjekteMaximum einer Collection von Zahlen?Wie würde man das in Java machen?If, Hi...
(reduce max [35 62 -12 43 56 7])Funktional
Reducereduce(reduce f coll)(reduce f val coll)f should be a function of 2 arguments. If val is not supplied,returns the re...
SortierenSortieren einer Liste von Geboten nach :bid (invers) und :timestamp (ältestes zuerst)(first (sort-by   (juxt (com...
comp - Funktionskompositionpartial - partial function application (fixesarguments)juxt - juxtaposition of functions(„Neben...
Fibonacci:In Java...Funktional
In Clojure - trivial:(defn fibonacci [a]  (cond    (> a 1) (+ (fibonacci (- a 2)) (fibonacci (- a 1)))    (= a 1) 1    (= ...
In Clojure - clojure style(def fib-seq ((fn rfib [a b]   (lazy-seq (cons a (rfib b (+ a b)))))  0 1))(take 20 fib-seq)Funk...
Domänenmodell
Map kann beliebige Elemente als Werte enthalten (dynamische Typisierung): {:first-name „Christian“,  :age 39,  :married tr...
Maps werden oft herangezogen, um domain- objects abzubilden. Keine Klassen und Objekte, sondern generische Maps. Später ev...
Spring: Eine unserer SorgenWir haben in Java-Projekten immer stark aufSpring gesetzt. Wie macht man das in Clojure?Inversi...
Inversion of ControlReusable code controls the execution ofproblem-specific codeSpring
Inversion of ControlAber das ist doch genau funktionales Programmieren...;; Create a word frequency map out of a large str...
Dependency InjectionZentral(declare create-person)Im „Nutzcode“(create-person „Christian“ „Betz“ :age 39)In der Definition...
Wo Licht ist …              oder Was Java-Entwickler vermissen
Übersicht Es gibt keine Klassendefinition, also nicht zwingend eine „Übersicht“ darüber, wie „Objekte“ aufgebaut sind.Lich...
Statische Typisierung Natürlich bietet ein statisches Typsystem auch Sicherheit - Typverletzungen erkennt schon der Compil...
Tooling Die Tools sind noch nicht so ausgereift wie bei Java. Insbesondere Refactoring Tools vermissen wir manchmal.Licht ...
Unsere „Lösung“ - automatisierte Tests   (braucht man auch in Java) - Dokumentation - Validierungsfunktionen   (braucht ma...
Und wie entwickelt man?
Test-driven development?
REPL-driven development!
REPL und TestsInteraktive Entwicklung von Funktionen in der REPLErhöht die Testbarkeit, weil man in der REPL ja nicht imme...
Das Clojure EcosystemProjektmanagement: lein (maven wie es sein sollte)Entwicklungsumgebung: Emacs, IntelliJ, EclipseTestf...
Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of ...
Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of ...
Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of ...
Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of ...
Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of ...
user=> (apropos+ "*warn")(#clojure.core/*warn-on-reflection*)user=> (apropos+ "format")(#clojure.pprint/formatter-out #clo...
Recap
Insgesamt: Super-zufrieden.Aber: Es gab natürlich Probleme in der Adaption.Änderung der Denke notwendig... Arbeit ohne "ma...
Clojure-Dojo?
lein (https://github.com/technomancy/leiningen)~/.lein/profiles.clj:{:user {:plugins [[lein-midje "2.0.0-SNAPSHOT"][lein-n...
Mehr Infos...?
Full Disclojure (Vimeo)http://vimeo.com/channels/fulldisclojure/videosClojure - Functional Programming for the JVMhttp://j...
Danke !Images:http://www.flickr.com/photos/marcp_dmoz/4138211812http://www.flickr.com/photos/b-natix/5249425587http://www....
Upcoming SlideShare
Loading in …5
×

Funktionales Programmieren mit Clojure

761 views

Published on

Überblick über die Entwicklung mit Clojure bei HEROLABS.

* Warum haben wir uns für Clojure entschieden? (Simplicity, Erweiterbarkeit, Java-Interop)

* Was heißt Funktionale Programmierung?

=> Man braucht ein anderes Mindset

* Was uns stört.

* Und wie entwickelt man mit Clojure (Ecosystem)?

Anlass war ein Talk bei mgm-tp.

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
761
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
8
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Funktionales Programmieren mit Clojure

  1. 1. CHANGE YOUR MINDSETFunktionales Programmieren mit Clojure chris_betz xing.to/betz
  2. 2. About me Promotion in KI - und ca. 10 Jahre Erfahrung mit Common LISP eCommerce/Finance mit SinnerSchrader - in Java Security / Visual Analytics bei Plath Mobile Entertainment bei HEROLABS - mit Clojure
  3. 3. HERO11 Mobile Sports Entertainment „Beat the coach“, live während reale Fußballspiele laufen iOS Client, Clojure Server, MongoDB Backend Typische Aufgaben: Processing eines Streams von Fußball-Events, Fanout von Scoring an alle Nutzer
  4. 4. Warum Clojure?Start „auf der grünen Wiese“: Freie Wahl derSpracheScala? oder Clojure? oder Go?Zwei Entwickler „pro Clojure“ (beide mit LISP-Erfahrung), einer „pro Scala“
  5. 5. Was machen andere damit?Twitter: big data processing (war Backtype:Storm)Flightcaster: realtime flight delay predictionRuna: real-time targeted offers based onstatistical models (e-Commerce)Prismatic: personal news aggregation
  6. 6. Was uns fasziniert...
  7. 7. SIMPLICITY
  8. 8. Reduktion aufs Wesentliche Java: class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); } } Clojure: (println "Hello World!")SIMPLICITY
  9. 9. Erweitertes Beispiel, aus : Programming Clojure, Stuart HallowaySIMPLICITY
  10. 10. org.apache.commons.lang.StringUtils: indexOfAny - findet in einem String das erste Vorkommen eines Zeichens aus einer vorgegebenen Menge StringUtils.indexOfAny(null, *) = -1 StringUtils.indexOfAny("", *) = -1 StringUtils.indexOfAny(*, null) = -1 StringUtils.indexOfAny(*, []) = -1 StringUtils.indexOfAny("zzabyycdxx",[z,a]) = 0 StringUtils.indexOfAny("zzabyycdxx",[b,y]) = 3 StringUtils.indexOfAny("aba", [z]) = -1SIMPLICITY
  11. 11. public static int indexOfAny(String str, char[] searchChars) { if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) { return -1; } for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); for (int j = 0; j < searchChars.length; j++) { if (searchChars[j] == ch) { return i; } return -1; } } }SIMPLICITY
  12. 12. Wie macht man das in Clojure?SIMPLICITY
  13. 13. (defn indexed [coll] (map vector (iterate inc 0) coll)) (indexed "abcde") ([0 a] [1 b] [2 c] [3 d] [4 e])SIMPLICITY
  14. 14. (defn index-filter [pred coll] (when pred (for [[idx elt] (indexed coll) :when (pred elt)] idx))) (index-filter #{a b} "abcdbbb") (0 1 4 5 6) (index-filter #{a b} "xyz") ()SIMPLICITY
  15. 15. (defn index-of-any [pred coll] (first (index-filter pred coll))) (index-of-any #{z a} "zzabyycdxx") 0 (index-of-any #{b y} "zzabyycdxx") 3SIMPLICITY
  16. 16. (defn indexed [coll] (map vector (iterate inc 0) coll)) (defn index-filter [pred coll] (when pred (for [[idx elt] (indexed coll) :when (pred elt)] idx))) (defn index-of-any [pred coll] (first (index-filter pred coll)))SIMPLICITY
  17. 17. Simplicity Exits/ Metrik LOC Branches Variables Method Java 14 4 3 3 imperativ Clojure 6 1 1 0 funktionalSIMPLICITY
  18. 18. weitere UnterschiedeSIMPLICITY
  19. 19. weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequencesSIMPLICITY
  20. 20. weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach einem Set von Characters, index-of-any kann beliebige Prädikate verwendenSIMPLICITY
  21. 21. weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach einem Set von Characters, index-of-any kann beliebige Prädikate verwenden indexOfAny liefert den ersten Treffer, index-filter liefert alle Treffer und kann mit anderen Filtern kombiniert werden.SIMPLICITY
  22. 22. weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach einem Set von Characters, index-of-any kann beliebige Prädikate verwenden indexOfAny liefert den ersten Treffer, index-filter liefert alle Treffer und kann mit anderen Filtern kombiniert werden. find the third occurrence of “heads” in a series of coin flips: (nth (index-filter #{:h} [:t :t :h :t :h :t :t :t :h :h]) 2) 8SIMPLICITY
  23. 23. weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences indexOfAny sucht nach einem Set von Characters, index-of-any kann beliebige Prädikate verwenden indexOfAny liefert den ersten Treffer, index-filter liefert alle Treffer und kann mit anderen Filtern kombiniert werden. find the third occurrence of “heads” in a series of coin flips: one, (nth (index-filter #{:h} [:t :t :h :t :h :t :t :t :h :h]) 2) pr 8 s er ror er, les si mpl gene ralSIMPLICITY and more
  24. 24. lazy sequences Natürlich werden in index-of-any nicht erst alle Treffer erzeugt und dann alle bis auf den ersten weggeworfen! Siehe auch infinite sequences...SIMPLICITY
  25. 25. for: Sequence comprehension syntaktisches Konstrukt zum Erzeugen einer Liste aus existierenden Listen entlehnt aus mathematischer „set notation“ S = {2*x | x ∈ℕ, x² > 3}SIMPLICITY
  26. 26. Unsere! Sprache
  27. 27. Die Sprache ist vollständig erweiterbar.Erweiterungen sind vom eigentlichenSprachkern nicht zu unterscheiden.UNSERE! Sprache
  28. 28. Beispiel aus dem Noir Webframework:(defpage „/person/:id.html“ [id]...)UNSERE! Sprache
  29. 29. Beispiel aus Monger:;; find scores 10 to 20(with-collection "scores"  (find {})  (fields ,,, [:score :name])  (sort ,,, {:score -1})  (limit ,,, 10)  (skip ,,, 10))UNSERE! Sprache
  30. 30. Hintergrund der Erweiterbarkeit- Homoiconicity: Code is Data- Das mächtigste Makrosystem: Clojure verwenden, um Clojure zu erzeugen„The whole language there all the time.“ P. GrahamUNSERE! Sprache
  31. 31. Java-Interop
  32. 32. alle Java - Libraries stehen zur Verfügungviele wichtige Libs mit clj-wrapperninteressante Libs in Clojure:noir (Web Framework), avout (noch sehr beta),congomongo bzw. monger (MongoDB)Java-Interop
  33. 33. Aber:Man braucht einanderes Mindset
  34. 34. Funktional- was heisst das?
  35. 35. Funktionen als First-class ObjekteMaximum einer Collection von Zahlen?Wie würde man das in Java machen?Funktional
  36. 36. Funktionen als First-class ObjekteMaximum einer Collection von Zahlen?Wie würde man das in Java machen?If, Hilfsvariable, Schleife, ...Funktional
  37. 37. (reduce max [35 62 -12 43 56 7])Funktional
  38. 38. Reducereduce(reduce f coll)(reduce f val coll)f should be a function of 2 arguments. If val is not supplied,returns the result of applying f to the first 2 items in coll, thenapplying f to that result and the 3rd item, etc. If coll contains noitems, f must accept no arguments as well, and reduce returns theresult of calling f with no arguments. If coll has only 1 item, itis returned and f is not called. If val is supplied, returns theresult of applying f to val and the first item in coll, thenapplying f to that result and the 2nd item, etc. If coll contains noitems, returns val and f is not called.Funktional
  39. 39. SortierenSortieren einer Liste von Geboten nach :bid (invers) und :timestamp (ältestes zuerst)(first (sort-by (juxt (comp (partial * -1) :bid) :timestamp) [{:id "af1" :bid 1 :timestamp 1} {:id "ba3" :bid 12 :timestamp 3} {:id "cd7" :bid 12 :timestamp 2}]))Und ja, normalerweise machen wir das auch in der Datenbank ;)Funktional
  40. 40. comp - Funktionskompositionpartial - partial function application (fixesarguments)juxt - juxtaposition of functions(„Nebeneinanderstellung“)Funktional
  41. 41. Fibonacci:In Java...Funktional
  42. 42. In Clojure - trivial:(defn fibonacci [a] (cond (> a 1) (+ (fibonacci (- a 2)) (fibonacci (- a 1))) (= a 1) 1 (= a 0) 0)Funktional
  43. 43. In Clojure - clojure style(def fib-seq ((fn rfib [a b] (lazy-seq (cons a (rfib b (+ a b))))) 0 1))(take 20 fib-seq)Funktional
  44. 44. Domänenmodell
  45. 45. Map kann beliebige Elemente als Werte enthalten (dynamische Typisierung): {:first-name „Christian“, :age 39, :married true}Domänenmodell
  46. 46. Maps werden oft herangezogen, um domain- objects abzubilden. Keine Klassen und Objekte, sondern generische Maps. Später evtl. ersetzt durch records (die dann intern in Java-Klassen abgebildet werden). Achtung: Lokalität! Wo erzeuge ich Objekte? (encapsulation, ...)Domänenmodell
  47. 47. Spring: Eine unserer SorgenWir haben in Java-Projekten immer stark aufSpring gesetzt. Wie macht man das in Clojure?Inversion of ControlDependency Injection(um gegen Interface entwickeln zu können,nicht konkrete Klassen)
  48. 48. Inversion of ControlReusable code controls the execution ofproblem-specific codeSpring
  49. 49. Inversion of ControlAber das ist doch genau funktionales Programmieren...;; Create a word frequency map out of a large string s.;; `s` is a long string containing a lot of words :)(reduce #(assoc %1 %2 (inc (%1 %2 0))) {} (re-seq #"w+" s)); (This can also be done using the `frequencies` function.)Spring
  50. 50. Dependency InjectionZentral(declare create-person)Im „Nutzcode“(create-person „Christian“ „Betz“ :age 39)In der Definition(defn create-person [first-name last-name ...] ...)Spring
  51. 51. Wo Licht ist … oder Was Java-Entwickler vermissen
  52. 52. Übersicht Es gibt keine Klassendefinition, also nicht zwingend eine „Übersicht“ darüber, wie „Objekte“ aufgebaut sind.Licht und SCHATTEN
  53. 53. Statische Typisierung Natürlich bietet ein statisches Typsystem auch Sicherheit - Typverletzungen erkennt schon der Compiler.Licht und SCHATTEN
  54. 54. Tooling Die Tools sind noch nicht so ausgereift wie bei Java. Insbesondere Refactoring Tools vermissen wir manchmal.Licht und SCHATTEN
  55. 55. Unsere „Lösung“ - automatisierte Tests (braucht man auch in Java) - Dokumentation - Validierungsfunktionen (braucht man sowieso)Licht und SCHATTEN
  56. 56. Und wie entwickelt man?
  57. 57. Test-driven development?
  58. 58. REPL-driven development!
  59. 59. REPL und TestsInteraktive Entwicklung von Funktionen in der REPLErhöht die Testbarkeit, weil man in der REPL ja nicht immer das gesamte Systemhochfahren möchte.Wenn man mit einem ersten Draft zufrieden ist, dann macht man Tests daraus,verfeinert diese und verbessert die Funktion.Das Ergebnis ist eine Funktion und ein Satz von Unit-TestsMein Liebling in schwierigen Fällen: (debug-repl)https://github.com/GeorgeJahad/debug-repl
  60. 60. Das Clojure EcosystemProjektmanagement: lein (maven wie es sein sollte)Entwicklungsumgebung: Emacs, IntelliJ, EclipseTestframework: midjeWeb-Framework: NoirHosting: Erst Heroku, jetzt AWS, Automatisierung mitJenkins, Pallet
  61. 61. Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of all definitions in all currently-loaded namespaces that match the str-or-pattern."  [str-or-pattern]  (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)    #(re-find str-or-pattern (str (key %)))    #(.contains (str (key %)) (str str-or-pattern)))]    (for [ns (all-ns)          public (ns-publics ns)          :when (matches? public)]      (second public))));; (in-ns user);; (use dev.apropos);; (apropos+ "*warn")
  62. 62. Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of all definitions in all currently-loaded namespaces that match the str-or-pattern."  [str-or-pattern]  (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)    #(re-find str-or-pattern (str (key %)))    #(.contains (str (key %)) (str str-or-pattern)))]    (for [ns (all-ns)          public (ns-publics ns)          :when (matches? public)]      (second public))));; (in-ns user);; (use dev.apropos);; (apropos+ "*warn")
  63. 63. Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of all definitions in all currently-loaded namespaces that match the str-or-pattern."  [str-or-pattern]  (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)    #(re-find str-or-pattern (str (key %)))    #(.contains (str (key %)) (str str-or-pattern)))]    (for [ns (all-ns)          public (ns-publics ns)          :when (matches? public)]      (second public))));; (in-ns user);; (use dev.apropos);; (apropos+ "*warn")
  64. 64. Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of all definitions in all currently-loaded namespaces that match the str-or-pattern."  [str-or-pattern]  (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)    #(re-find str-or-pattern (str (key %)))    #(.contains (str (key %)) (str str-or-pattern)))]    (for [ns (all-ns)          public (ns-publics ns)          :when (matches? public)]      (second public))));; (in-ns user);; (use dev.apropos);; (apropos+ "*warn")
  65. 65. Hacks explained: apropos+(ns dev.apropos)(defn apropos+  "Given a regular expression or stringable thing, return a seq of all definitions in all currently-loaded namespaces that match the str-or-pattern."  [str-or-pattern]  (let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)    #(re-find str-or-pattern (str (key %)))    #(.contains (str (key %)) (str str-or-pattern)))]    (for [ns (all-ns)          public (ns-publics ns)          :when (matches? public)]      (second public))));; (in-ns user);; (use dev.apropos);; (apropos+ "*warn")
  66. 66. user=> (apropos+ "*warn")(#clojure.core/*warn-on-reflection*)user=> (apropos+ "format")(#clojure.pprint/formatter-out #clojure.pprint/formatter #clojure.pprint/cl-format #clojure.core/format)
  67. 67. Recap
  68. 68. Insgesamt: Super-zufrieden.Aber: Es gab natürlich Probleme in der Adaption.Änderung der Denke notwendig... Arbeit ohne "malschnell eine Variable hochzählen" ist nicht immereinfach.Weniger Code (d.h. weniger zu lesen und zu warten,was 90% der Arbeit ausmacht)More fun - just dive into your program using the REPL
  69. 69. Clojure-Dojo?
  70. 70. lein (https://github.com/technomancy/leiningen)~/.lein/profiles.clj:{:user {:plugins [[lein-midje "2.0.0-SNAPSHOT"][lein-noir "1.2.1"]]}}noir (http://webnoir.org)foundation: (http://foundation.zurb.com/)Branch: http://branch.com/featured-> lein new noir gwitterhttp://localhost:8080/
  71. 71. Mehr Infos...?
  72. 72. Full Disclojure (Vimeo)http://vimeo.com/channels/fulldisclojure/videosClojure - Functional Programming for the JVMhttp://java.ociweb.com/mark/clojure/article.htmlClojure Docshttp://clojuredocs.org/Closure/West Conference (Slides in github)http://clojurewest.org@planetclojure, @fogus, @stuarthalloway, ...
  73. 73. Danke !Images:http://www.flickr.com/photos/marcp_dmoz/4138211812http://www.flickr.com/photos/b-natix/5249425587http://www.flickr.com/photos/stuckincustoms/4208255182http://www.flickr.com/photos/abbyladybug/491270355http://www.flickr.com/photos/petereed/496392956/http://www.flickr.com/photos/visualpanic/2394430691http://www.flickr.com/photos/bitzcelt/2762928930http://www.flickr.com/photos/tobstone/84696661http://www.flickr.com/photos/pulpolux/1306334352/http://www.flickr.com/photos/yashna13/5101434185/http://www.flickr.com/photos/go_freyer/3185104403http://www.flickr.com/photos/seier/4797164691/http://www.flickr.com/photos/visualpanic/2512530843http://www.flickr.com/photos/zeze57/5717675695

×