SlideShare ist ein Scribd-Unternehmen logo
1 von 73
Downloaden Sie, um offline zu lesen
CHANGE YOUR MINDSET
Funktionales 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
 Security / Visual Analytics bei Plath
 Mobile Entertainment bei HEROLABS
 - mit Clojure
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
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“
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
Was uns fasziniert...
SIMPLICITY
Reduktion aufs Wesentliche
 Java:
 class HelloWorldApp {
   public static void main(String[] args) {
     System.out.println("Hello World!");
   }
 }




 Clojure:
 (println "Hello World!")




SIMPLICITY
Erweitertes Beispiel,
 aus : Programming Clojure, Stuart Halloway




SIMPLICITY
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
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
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 b} "abcdbbb")
    (0 1 4 5 6)
 (index-filter #{a b} "xyz")
    ()



SIMPLICITY
(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
(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
Simplicity
                            Exits/
   Metrik     LOC Branches         Variables
                           Method

    Java
              14      4        3       3
  imperativ

   Clojure
               6      1        1       0
 funktional



SIMPLICITY
weitere Unterschiede




SIMPLICITY
weitere Unterschiede
 indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences




SIMPLICITY
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
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
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
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 ral
SIMPLICITY                and  more
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
for: Sequence comprehension


 syntaktisches Konstrukt zum Erzeugen einer
 Liste aus existierenden Listen
 entlehnt aus mathematischer „set notation“
 S = {2*x | x ∈ℕ, x² > 3}



SIMPLICITY
Unsere! Sprache
Die Sprache ist vollständig erweiterbar.
Erweiterungen sind vom eigentlichen
Sprachkern 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 ,,, {:score -1})
  (limit ,,, 10)
  (skip ,,, 10))



UNSERE! Sprache
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. Graham


UNSERE! Sprache
Java-Interop
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
Aber:

Man braucht ein
anderes Mindset
Funktional
- was heisst
    das?
Funktionen als First-class
              Objekte


Maximum einer Collection von Zahlen?
Wie würde man das in Java machen?




Funktional
Funktionen als First-class
              Objekte


Maximum einer Collection von Zahlen?
Wie würde man das in Java machen?
If, Hilfsvariable, Schleife, ...



Funktional
(reduce max [35 62 -12 43 56 7])




Funktional
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
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
comp - Funktionskomposition
partial - partial function application (fixes
arguments)
juxt - juxtaposition of functions
(„Nebeneinanderstellung“)




Funktional
Fibonacci:
In Java...




Funktional
In Clojure - trivial:
(defn fibonacci [a]
  (cond
    (> a 1) (+ (fibonacci (- a 2)) (fibonacci (- a 1)))
    (= a 1) 1
    (= a 0) 0)




Funktional
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
Domänenmodell
Map kann beliebige Elemente als Werte
 enthalten (dynamische Typisierung):
 {:first-name „Christian“,
  :age 39,
  :married true}




Domänenmodell
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
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)
Inversion of Control


Reusable code controls the execution of
problem-specific code




Spring
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
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
Wo Licht ist …
              oder
 Was Java-Entwickler vermissen
Übersicht


 Es gibt keine Klassendefinition, also nicht
 zwingend eine „Übersicht“ darüber, wie
 „Objekte“ aufgebaut sind.




Licht und SCHATTEN
Statische Typisierung


 Natürlich bietet ein statisches Typsystem auch
 Sicherheit - Typverletzungen erkennt schon der
 Compiler.




Licht und SCHATTEN
Tooling


 Die Tools sind noch nicht so ausgereift wie bei
 Java. Insbesondere Refactoring Tools vermissen
 wir manchmal.




Licht und SCHATTEN
Unsere „Lösung“

 - automatisierte Tests
   (braucht man auch in Java)
 - Dokumentation
 - Validierungsfunktionen
   (braucht man sowieso)


Licht und SCHATTEN
Und wie entwickelt man?
Test-driven development?
REPL-driven development!
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 Draft 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
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
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")
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")
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")
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")
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")
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)
Recap
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
Clojure-Dojo?
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/
Mehr Infos...?
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, ...
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

Weitere ähnliche Inhalte

Was ist angesagt?

T2 s4 javascriptfuerfortgeschrittene
T2 s4 javascriptfuerfortgeschritteneT2 s4 javascriptfuerfortgeschrittene
T2 s4 javascriptfuerfortgeschrittenedominion
 
Was geht mit Java 17?
Was geht mit Java 17?Was geht mit Java 17?
Was geht mit Java 17?gedoplan
 
Java Streams und Lambdas
Java Streams und LambdasJava Streams und Lambdas
Java Streams und LambdasNane Kratzke
 
Metaprogrammierung mit Ruby
Metaprogrammierung mit RubyMetaprogrammierung mit Ruby
Metaprogrammierung mit RubyDario Rexin
 
Einführung in die funktionale Programmierung mit Clojure
Einführung in die funktionale Programmierung mit ClojureEinführung in die funktionale Programmierung mit Clojure
Einführung in die funktionale Programmierung mit ClojureSascha Koch
 
Java Script Ist Anders
Java Script Ist AndersJava Script Ist Anders
Java Script Ist Andersjlink
 
Perl 5.20: Feature, Kultur, Module, Werkzeuge
Perl 5.20: Feature, Kultur, Module, WerkzeugePerl 5.20: Feature, Kultur, Module, Werkzeuge
Perl 5.20: Feature, Kultur, Module, Werkzeugelichtkind
 
Mein paralleles Leben als Java-Entwickler
Mein paralleles Leben als Java-EntwicklerMein paralleles Leben als Java-Entwickler
Mein paralleles Leben als Java-Entwicklerjlink
 
Nigh Session Scala
Nigh Session ScalaNigh Session Scala
Nigh Session Scalaadesso AG
 
Tech Talk: Groovy
Tech Talk: GroovyTech Talk: Groovy
Tech Talk: Groovymwie
 
Einführung in die funktionale Programmierung
Einführung in die funktionale ProgrammierungEinführung in die funktionale Programmierung
Einführung in die funktionale ProgrammierungDigicomp Academy AG
 
Doctrine 2 - An Introduction (German)
Doctrine 2 - An Introduction (German)Doctrine 2 - An Introduction (German)
Doctrine 2 - An Introduction (German)Michael Romer
 
An Introduction to Ruby
An Introduction to RubyAn Introduction to Ruby
An Introduction to RubyJonathan Weiss
 

Was ist angesagt? (20)

Slides
SlidesSlides
Slides
 
T2 s4 javascriptfuerfortgeschrittene
T2 s4 javascriptfuerfortgeschritteneT2 s4 javascriptfuerfortgeschrittene
T2 s4 javascriptfuerfortgeschrittene
 
Was geht mit Java 17?
Was geht mit Java 17?Was geht mit Java 17?
Was geht mit Java 17?
 
Java Streams und Lambdas
Java Streams und LambdasJava Streams und Lambdas
Java Streams und Lambdas
 
Metaprogrammierung mit Ruby
Metaprogrammierung mit RubyMetaprogrammierung mit Ruby
Metaprogrammierung mit Ruby
 
Einführung in die funktionale Programmierung mit Clojure
Einführung in die funktionale Programmierung mit ClojureEinführung in die funktionale Programmierung mit Clojure
Einführung in die funktionale Programmierung mit Clojure
 
Java Script Ist Anders
Java Script Ist AndersJava Script Ist Anders
Java Script Ist Anders
 
BIT I WiSe 2014 | Basisinformationstechnologie I - 10: Programmiersprachen III
BIT I WiSe 2014 | Basisinformationstechnologie I - 10: Programmiersprachen IIIBIT I WiSe 2014 | Basisinformationstechnologie I - 10: Programmiersprachen III
BIT I WiSe 2014 | Basisinformationstechnologie I - 10: Programmiersprachen III
 
Perl 5.20: Feature, Kultur, Module, Werkzeuge
Perl 5.20: Feature, Kultur, Module, WerkzeugePerl 5.20: Feature, Kultur, Module, Werkzeuge
Perl 5.20: Feature, Kultur, Module, Werkzeuge
 
Typescript
TypescriptTypescript
Typescript
 
Mein paralleles Leben als Java-Entwickler
Mein paralleles Leben als Java-EntwicklerMein paralleles Leben als Java-Entwickler
Mein paralleles Leben als Java-Entwickler
 
Nigh Session Scala
Nigh Session ScalaNigh Session Scala
Nigh Session Scala
 
Ctypes
CtypesCtypes
Ctypes
 
Ruby, Ruby, Ruby!
Ruby, Ruby, Ruby!Ruby, Ruby, Ruby!
Ruby, Ruby, Ruby!
 
Tech Talk: Groovy
Tech Talk: GroovyTech Talk: Groovy
Tech Talk: Groovy
 
JavaSPEKTRUM - Scala 3
JavaSPEKTRUM - Scala 3JavaSPEKTRUM - Scala 3
JavaSPEKTRUM - Scala 3
 
Do while
Do whileDo while
Do while
 
Einführung in die funktionale Programmierung
Einführung in die funktionale ProgrammierungEinführung in die funktionale Programmierung
Einführung in die funktionale Programmierung
 
Doctrine 2 - An Introduction (German)
Doctrine 2 - An Introduction (German)Doctrine 2 - An Introduction (German)
Doctrine 2 - An Introduction (German)
 
An Introduction to Ruby
An Introduction to RubyAn Introduction to Ruby
An Introduction to Ruby
 

Ähnlich wie Funktionales Programmieren mit Clojure

Go - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare SystemeGo - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare SystemeFrank Müller
 
Architektur einer Eclipse DLTK IDE für Clojure
Architektur einer Eclipse DLTK IDE für ClojureArchitektur einer Eclipse DLTK IDE für Clojure
Architektur einer Eclipse DLTK IDE für ClojureMatthias Köster
 
PyLucene@PyCon DE 2011
PyLucene@PyCon DE 2011PyLucene@PyCon DE 2011
PyLucene@PyCon DE 2011Thomas Koch
 
Java und Go im Vergleich
Java und Go im VergleichJava und Go im Vergleich
Java und Go im VergleichQAware GmbH
 
SCALA: ein kurzer Überblick
SCALA: ein kurzer ÜberblickSCALA: ein kurzer Überblick
SCALA: ein kurzer ÜberblickRoland Ewald
 
Einfacher bauen
Einfacher bauenEinfacher bauen
Einfacher bauenjohofer
 
Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0Qiong Wu
 
The Lotus Code Cookbook
The Lotus Code CookbookThe Lotus Code Cookbook
The Lotus Code CookbookUlrich Krause
 
Scala - A Scalable Language
Scala - A Scalable LanguageScala - A Scalable Language
Scala - A Scalable Languagepfleidi
 
Besseren Java Code mit Type Annotations
Besseren Java Code mit Type AnnotationsBesseren Java Code mit Type Annotations
Besseren Java Code mit Type Annotationsdzuvic
 
Roslyn - Ein offener Compiler. Ja, und nun?
Roslyn - Ein offener Compiler. Ja, und nun?Roslyn - Ein offener Compiler. Ja, und nun?
Roslyn - Ein offener Compiler. Ja, und nun?Robin Sedlaczek
 
A fool with a tool
A fool with a toolA fool with a tool
A fool with a toolMarkus Uhl
 
LINQ - Einheitlicher Datenzugriff in .NET
LINQ - Einheitlicher Datenzugriff in .NETLINQ - Einheitlicher Datenzugriff in .NET
LINQ - Einheitlicher Datenzugriff in .NETGFU Cyrus AG
 
Fundamentale Muster in Java
Fundamentale Muster in JavaFundamentale Muster in Java
Fundamentale Muster in Javatutego
 
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...GFU Cyrus AG
 
Devs@Home - Einführung in Go
Devs@Home - Einführung in GoDevs@Home - Einführung in Go
Devs@Home - Einführung in GoFrank Müller
 
TYPO3 coding guidelines
TYPO3 coding guidelinesTYPO3 coding guidelines
TYPO3 coding guidelinesAlex Kellner
 

Ähnlich wie Funktionales Programmieren mit Clojure (20)

Scala und Lift
Scala und LiftScala und Lift
Scala und Lift
 
Go - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare SystemeGo - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare Systeme
 
Architektur einer Eclipse DLTK IDE für Clojure
Architektur einer Eclipse DLTK IDE für ClojureArchitektur einer Eclipse DLTK IDE für Clojure
Architektur einer Eclipse DLTK IDE für Clojure
 
PyLucene@PyCon DE 2011
PyLucene@PyCon DE 2011PyLucene@PyCon DE 2011
PyLucene@PyCon DE 2011
 
Java und Go im Vergleich
Java und Go im VergleichJava und Go im Vergleich
Java und Go im Vergleich
 
Scala
ScalaScala
Scala
 
SCALA: ein kurzer Überblick
SCALA: ein kurzer ÜberblickSCALA: ein kurzer Überblick
SCALA: ein kurzer Überblick
 
Einfacher bauen
Einfacher bauenEinfacher bauen
Einfacher bauen
 
Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0
 
The Lotus Code Cookbook
The Lotus Code CookbookThe Lotus Code Cookbook
The Lotus Code Cookbook
 
Scala - A Scalable Language
Scala - A Scalable LanguageScala - A Scalable Language
Scala - A Scalable Language
 
Ein Gopher im Netz
Ein Gopher im NetzEin Gopher im Netz
Ein Gopher im Netz
 
Besseren Java Code mit Type Annotations
Besseren Java Code mit Type AnnotationsBesseren Java Code mit Type Annotations
Besseren Java Code mit Type Annotations
 
Roslyn - Ein offener Compiler. Ja, und nun?
Roslyn - Ein offener Compiler. Ja, und nun?Roslyn - Ein offener Compiler. Ja, und nun?
Roslyn - Ein offener Compiler. Ja, und nun?
 
A fool with a tool
A fool with a toolA fool with a tool
A fool with a tool
 
LINQ - Einheitlicher Datenzugriff in .NET
LINQ - Einheitlicher Datenzugriff in .NETLINQ - Einheitlicher Datenzugriff in .NET
LINQ - Einheitlicher Datenzugriff in .NET
 
Fundamentale Muster in Java
Fundamentale Muster in JavaFundamentale Muster in Java
Fundamentale Muster in Java
 
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
 
Devs@Home - Einführung in Go
Devs@Home - Einführung in GoDevs@Home - Einführung in Go
Devs@Home - Einführung in Go
 
TYPO3 coding guidelines
TYPO3 coding guidelinesTYPO3 coding guidelines
TYPO3 coding guidelines
 

Funktionales Programmieren mit Clojure

  • 1. CHANGE YOUR MINDSET Funktionales Programmieren mit Clojure chris_betz xing.to/betz
  • 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. 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. 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“
  • 5. 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
  • 8. Reduktion aufs Wesentliche Java: class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); } } Clojure: (println "Hello World!") SIMPLICITY
  • 9. Erweitertes Beispiel, aus : Programming Clojure, Stuart Halloway SIMPLICITY
  • 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']) = -1 SIMPLICITY
  • 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. Wie macht man das in Clojure? SIMPLICITY
  • 13. (defn indexed [coll] (map vector (iterate inc 0) coll)) (indexed "abcde") ([0 a] [1 b] [2 c] [3 d] [4 e]) SIMPLICITY
  • 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. (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
  • 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. Simplicity Exits/ Metrik LOC Branches Variables Method Java 14 4 3 3 imperativ Clojure 6 1 1 0 funktional SIMPLICITY
  • 19. weitere Unterschiede indexOfAny sucht nur in Strings, index-of-any durchsucht beliebige sequences SIMPLICITY
  • 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 verwenden SIMPLICITY
  • 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. 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
  • 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 ral SIMPLICITY and more
  • 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. for: Sequence comprehension syntaktisches Konstrukt zum Erzeugen einer Liste aus existierenden Listen entlehnt aus mathematischer „set notation“ S = {2*x | x ∈ℕ, x² > 3} SIMPLICITY
  • 27. Die Sprache ist vollständig erweiterbar. Erweiterungen sind vom eigentlichen Sprachkern nicht zu unterscheiden. UNSERE! Sprache
  • 28. Beispiel aus dem Noir Webframework: (defpage „/person/:id.html“ [id] ...) UNSERE! Sprache
  • 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. 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. Graham UNSERE! Sprache
  • 32. 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
  • 35. Funktionen als First-class Objekte Maximum einer Collection von Zahlen? Wie würde man das in Java machen? Funktional
  • 36. Funktionen als First-class Objekte Maximum einer Collection von Zahlen? Wie würde man das in Java machen? If, Hilfsvariable, Schleife, ... Funktional
  • 37. (reduce max [35 62 -12 43 56 7]) Funktional
  • 38. 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
  • 39. 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
  • 40. comp - Funktionskomposition partial - partial function application (fixes arguments) juxt - juxtaposition of functions („Nebeneinanderstellung“) Funktional
  • 42. In Clojure - trivial: (defn fibonacci [a] (cond (> a 1) (+ (fibonacci (- a 2)) (fibonacci (- a 1))) (= a 1) 1 (= a 0) 0) Funktional
  • 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
  • 45. Map kann beliebige Elemente als Werte enthalten (dynamische Typisierung): {:first-name „Christian“, :age 39, :married true} Domänenmodell
  • 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. 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)
  • 48. Inversion of Control Reusable code controls the execution of problem-specific code Spring
  • 49. 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
  • 50. 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
  • 51. Wo Licht ist … oder Was Java-Entwickler vermissen
  • 52. Übersicht Es gibt keine Klassendefinition, also nicht zwingend eine „Übersicht“ darüber, wie „Objekte“ aufgebaut sind. Licht und SCHATTEN
  • 53. Statische Typisierung Natürlich bietet ein statisches Typsystem auch Sicherheit - Typverletzungen erkennt schon der Compiler. Licht und SCHATTEN
  • 54. Tooling Die Tools sind noch nicht so ausgereift wie bei Java. Insbesondere Refactoring Tools vermissen wir manchmal. Licht und SCHATTEN
  • 55. Unsere „Lösung“ - automatisierte Tests (braucht man auch in Java) - Dokumentation - Validierungsfunktionen (braucht man sowieso) Licht und SCHATTEN
  • 59. 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 Draft 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
  • 60. 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
  • 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. 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. 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. 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. 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. 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. Recap
  • 68. 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
  • 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 gwitter http://localhost:8080/
  • 72. 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, ...