Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Change your Mindset!

Change your Mindset!

A recap of why we - at HEROLABS - chose Clojure in the first place and some of our experiences.

Dr. Christian Betz

September 26, 2012
Tweet

More Decks by Dr. Christian Betz

Other Decks in Programming

Transcript

  1. 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 About me
  2. 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 HERO11
  3. Warum Clojure? Start „auf der grünen Wiese“: Freie Wahl der

    Sprache Scala? oder Clojure? oder Go? Zwei Entwickler „pro Clojure“ (beide mit LISP- Erfahrung), einer „pro Scala“
  4. Was machen andere damit? Twitter: big data processing (war Backtype:

    Storm) Flightcaster: realtime flight delay prediction Runa: real-time targeted offers based on statistical models (e-Commerce) Prismatic: personal news aggregation
  5. Reduktion aufs Wesentliche Java: class HelloWorldApp { public static void

    main(String[] args) { System.out.println("Hello World!"); } } Clojure: (println "Hello World!") SIMPLICITY
  6. 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']) = -1 SIMPLICITY
  7. 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
  8. (defn indexed [coll] (map vector (iterate inc 0) coll)) (indexed

    "abcde") 㱺 ([0 \a] [1 \b] [2 \c] [3 \d] [4 \e]) SIMPLICITY
  9. (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
  10. (defn index-of-any [pred coll] (first (index-filter pred coll))) (index-of-any #{\z

    \a} "zzabyycdxx") 㱺0 (index-of-any #{\b \y} "zzabyycdxx") 㱺3 SIMPLICITY
  11. (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
  12. 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 SIMPLICITY
  13. 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
  14. 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) 㱺8 SIMPLICITY
  15. 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) 㱺8 simpler, less error prone, and more general SIMPLICITY
  16. 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
  17. for: Sequence comprehension syntaktisches Konstrukt zum Erzeugen einer Liste aus

    existierenden Listen entlehnt aus mathematischer „set notation“ S = {2*x | x ∈ℕ, x² > 3} SIMPLICITY
  18. Beispiel aus Monger: ;; find scores 10 to 20 (with-collection

    "scores" (find {}) (fields ,,, [:score :name]) (sort ,,, {:score -1}) (limit ,,, 10) (skip ,,, 10)) UNSERE! Sprache
  19. - Homoiconicity: Code is Data - Das mächtigste Makrosystem: Clojure

    verwenden, um Clojure zu erzeugen „The whole language there all the time.“ P. Graham UNSERE! Sprache Hintergrund der Erweiterbarkeit
  20. alle Java - Libraries stehen zur Verfügung viele wichtige Libs

    mit clj-wrappern interessante Libs in Clojure: noir (Web Framework), avout (noch sehr beta), congomongo bzw. monger (MongoDB) Java-Interop
  21. Funktionen als First-class Objekte Maximum einer Collection von Zahlen? Wie

    würde man das in Java machen? If, Hilfsvariable, Schleife, ... Funktional
  22. Reduce reduce (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, then applying f to that result and the 3rd item, etc. If coll contains no items, f must accept no arguments as well, and reduce returns the result of calling f with no arguments. If coll has only 1 item, it is returned and f is not called. If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc. If coll contains no items, returns val and f is not called. Funktional
  23. Sortieren Sortieren 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
  24. comp - Funktionskomposition partial - partial function application (fixes arguments)

    juxt - juxtaposition of functions („Nebeneinanderstellung“) Funktional
  25. In Clojure - trivial: (defn fibonacci [a] (cond (> a

    1) (+ (fibonacci (- a 2)) (fibonacci (- a 1))) (= a 1) 1 (= a 0) 0) Funktional
  26. 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
  27. Map kann beliebige Elemente als Werte enthalten (dynamische Typisierung): {:first-name

    „Christian“, :age 39, :married true} Domänenmodell
  28. Maps werden o 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
  29. Spring: Eine unserer Sorgen Wir haben in Java-Projekten immer stark

    auf Spring gesetzt. Wie macht man das in Clojure? Inversion of Control Dependency Injection (um gegen Interface entwickeln zu können, nicht konkrete Klassen)
  30. Inversion of Control Aber 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
  31. Dependency Injection Zentral (declare create-person) Im „Nutzcode“ (create-person „Christian“ „Betz“

    :age 39) In der Definition (defn create-person [first-name last-name ...] ...) Spring
  32. Übersicht Es gibt keine Klassendefinition, also nicht zwingend eine „Übersicht“

    darüber, wie „Objekte“ aufgebaut sind. Licht und SCHATTEN
  33. Statische Typisierung Natürlich bietet ein statisches Typsystem auch Sicherheit -

    Typverletzungen erkennt schon der Compiler. Licht und SCHATTEN
  34. Tooling Die Tools sind noch nicht so ausgerei wie bei

    Java. Insbesondere Refactoring Tools vermissen wir manchmal. Licht und SCHATTEN
  35. Unsere „Lösung“ - automatisierte Tests (braucht man auch in Java)

    - Dokumentation - Validierungsfunktionen (braucht man sowieso) Licht und SCHATTEN
  36. REPL und Tests Interaktive Entwicklung von Funktionen in der REPL

    Erhöht die Testbarkeit, weil man in der REPL ja nicht immer das gesamte System hochfahren möchte. Wenn man mit einem ersten Dra zufrieden ist, dann macht man Tests daraus, verfeinert diese und verbessert die Funktion. Das Ergebnis ist eine Funktion und ein Satz von Unit-Tests Mein Liebling in schwierigen Fällen: (debug-repl) https://github.com/GeorgeJahad/debug-repl
  37. Das Clojure Ecosystem Projektmanagement: lein (maven wie es sein sollte)

    Entwicklungsumgebung: Emacs, IntelliJ, Eclipse Testframework: midje Web-Framework: Noir Hosting: Erst Heroku, jetzt AWS, Automatisierung mit Jenkins, Pallet
  38. 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")
  39. 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")
  40. 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")
  41. 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")
  42. 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")
  43. Insgesamt: Super-zufrieden. Aber: Es gab natürlich Probleme in der Adaption.

    Änderung der Denke notwendig... Arbeit ohne "mal schnell eine Variable hochzählen" ist nicht immer einfach. 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
  44. 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 gwitter http://localhost:8080/
  45. Full Disclojure (Vimeo) http://vimeo.com/channels/fulldisclojure/videos Clojure - Functional Programming for the

    JVM http://java.ociweb.com/mark/clojure/article.html Clojure Docs http://clojuredocs.org/ Closure/West Conference (Slides in github) http://clojurewest.org @planetclojure, @fogus, @stuarthalloway, ...
  46. Danke ! Images: http://www.flickr.com/photos/marcp_dmoz/4138211812 http://www.flickr.com/photos/b-natix/5249425587 http://www.flickr.com/photos/stuckincustoms/4208255182 http://www.flickr.com/photos/abbyladybug/491270355 http://www.flickr.com/photos/petereed/496392956/ http://www.flickr.com/photos/visualpanic/2394430691 http://www.flickr.com/photos/bitzcelt/2762928930

    http://www.flickr.com/photos/tobstone/84696661 http://www.flickr.com/photos/pulpolux/1306334352/ http://www.flickr.com/photos/yashna13/5101434185/ http://www.flickr.com/photos/go_freyer/3185104403 http://www.flickr.com/photos/seier/4797164691/ http://www.flickr.com/photos/visualpanic/2512530843 http://www.flickr.com/photos/zeze57/5717675695