SlideShare une entreprise Scribd logo
1  sur  79
Télécharger pour lire hors ligne
Clojure  (oubliez vos préjugés)

Christophe Grand @cgrand
Laurent Petit @petitlaurent


Mix-it 2011, 5 avril
Motivations
Motivations
Réduire complexité accidentelle
Réduire la complexité accidentelle

"Out of the tar pit" ( http://ben.moseley.name/frp/paper-v1_01.pdf )
   Distinguer la complexité inhérente de l'accidentelle

"Le mythe du mois-homme" ( Frederick P. Brooks, Jr.)
   Ch. 16: "Pas de balle d'argent: l'essence et la
   substance en génie logiciel"
Réduire la complexité accidentelle

   "Out of the tar pit" ( http://ben.moseley.name/frp/paper-v1_01.pdf )
      Distinguer la complexité inhérente de l'accidentelle

   "Le mythe du mois-homme" ( Frederick P. Brooks, Jr.)
      Ch. 16: "Pas de balle d'argent: l'essence et la
      substance en génie logiciel"


"Comme Aristote, je divise [les difficultés de la technologie
logicielle] en essence - celles qui sont inhérentes à la
nature du logiciel - et en substance - celles qui gênent
aujourd'hui sa production, mais n'y sont pas inhérentes."
                            (F. P. Brooks dans Le mythe du mois-homme)
Réduire la complexité accidentelle



   Simple
        fiable
     orthogonal
   peu de syntaxe
   peu de concepts
  peu de surprises
 larges abstractions
Motivations
Réduire complexité accidentelle   Programmation concurrente
Programmation concurrente


Mécanismes de bas niveau (lock, synchronized)

OOP introduit de la complexité
  Myriades de petits états répartis très difficiles à
  manipuler de manière cohérente




"#antioopargs OO, as practiced e.g. in Java,
conflates identity, state and behavior"
                              @stilkov, expert java reconnu
Motivations
Réduire complexité accidentelle   Programmation concurrente




Expressivité maximale
Expressivité maximale du langage


Concision
  Le moins "cérémonieux possible" !

Factorisation
   Oser passer aux génériques !

Adaptation
   "Si le problème ne vient pas au langage, le langage
   ira au problème" !

Degrés d'abstraction
  Rester dans le langage, en bonne compagnie !
Expressivité maximale du langage


Approche combinée Top Down et Bottom Up
   Décomposition fonctionnelle .... (top down)
   Mais construction d'un "langage" pour le domaine du
   problème (bottom up)
   Equivalent des APIs dites "fluent" en java
  /* exemple d'API "fluent" dans JPA */
  em.createNamedQuery("Student.findByNameAgeGender")
    .setParameter("name", name)
    .setHint("hintName", "hintValue")
    .getResultList();




   = mini-langages "embarqués" (DSLs)
Expressivité maximale du langage


Encore 1 fois, l'OOP par défaut est dans le collimateur !

   Plus difficile de généraliser les algorithmes
   Plus cérémonieux

     Mais ne pas jeter le bébé avec l'eau du bain

"Composition, interfaces, héritage : 2 bonnes idées sur 3,
pourquoi tt le monde est parti avec la 3ème ?"
                                        "Vu sur Twitter" (#antioop)
Motivations
Réduire complexité accidentelle   Programmation concurrente




Expressivité maximale                 Langage généraliste
Langage généraliste

Plateforme industrielle
   interopérable
   performant
   déployable
   réutilisation de l'existant

Pas un langage de niche
   performant dans le cas général
   sans perdre les qualités du langage (pas de
   contorsions)
Motivations
Réduire complexité accidentelle   Programmation concurrente




Expressivité maximale                    Langage généraliste
Motivations
Réduire complexité accidentelle   Programmation concurrente




Expressivité maximale                    Langage généraliste
Motivations
Réduire complexité accidentelle   Programmation concurrente




Expressivité maximale                    Langage généraliste
Caractéristiques
Caractéristiques


Base fonctionnelle
Base fonctionnelle

"It is better to have 100 functions operate on one data
structure than to have 10 functions operate on 10 data
structures."
                                             - Alan J. Perlis
  Briques de base simples
      Fonctions
      Structures de données génériques

  Fonctions sans effet de bord
     Sur leurs arguments
     Sur leur environnement
     Retournant une valeur non altérable
     => Naturellement thread-safe !
Base fonctionnelle

 Sans hypothéquer le pouvoir d'abstraction/indirection
    Fonctions en paramètres
    Fonctions en valeur de retour ("closures")
    Fonctions polymorphes


 Application : le concept de "séquence" en Clojure:
"It is better to have 100 functions operate on one data
abstraction than 10 functions on 10 data structures"
                                    - Attributed to Rich Hickey
Caractéristiques


Base fonctionnelle

Etats Managés
Etats managés

La base fonctionnelle seule est insuffisante


Besoin d'orchestrer la mutation des états
   Pour écrire des programmes concurrents fiables
   Mais aussi pour bien organiser son code !


 Confiner la mutation des états comme on confine le
traitement des entrées-sorties !

On installe cette orchestration au coeur du langage =
idiomatique et orthogonal
Etats managés


   Séparation stricte identité / valeur

   Identité = stable au cours du temps
   Etat = Valeur des caractéristiques d'une identité à un
   instant t
   Valeur = ensemble immuable de caractéristiques

Le temps passe, les identités sont stables, leurs valeurs
changent
   PRIMITIVES DE HAUT NIVEAU POUR GERER LES
               TRANSITIONS D'ETAT
Caractéristiques


Base fonctionnelle

Etats Managés

Lisp
LISP

Syntaxe simple et uniforme
Rapproche donnée et code
   "Data is code, and code is data"

   En utilisant les macros à la compilation : "code
   writing code"
       Alternative à la répétition de certains patterns
       Possibilité de rester dans le langage plus
       longtemps (pas de génération de code depuis
       UML !)

    Suppression possible des derniers
         "boilerplates" du code
LISP

Dynamique
   REPL = "Read, Eval, Print, Loop"
      Le code est évalué (en fait compilé) ligne à ligne
      On peut continuer d'évaluer du nouveau code au
      runtime
      On peut recharger les valeurs des
      fonctions/variables à chaud
      On peut évaluer n'importe quel code de
      test/initialisation à chaud
   Très pratique pour le prototypage, le debug ....


               Un langage "AGILE" !
Caractéristiques


Base fonctionnelle

Etats Managés

Lisp

JVM
JVM




 JVM
pragmatisme                   Compilé
écosystème                 interactif ou AOT
 entreprise               *warn-on-reflection*



       Performant
         Java en benchmark
           Hotspot-friendly
JVM




                Interop
Zéro overhead   java.util.*   Runnable & Callable
Caractéristiques


Base fonctionnelle

Etats Managés

Lisp

JVM
Syntaxe
Commentaire ; ligne             #_(bloc de code)

         Chaîne "Bonjour Lyon !nBonjour Mix-IT !"

       Nombres 42 2/3 3.14 1e6 12.34M 42N

     Caractères a newline space

      Booléens true false

            null nil

       Regexes #"a*b*"         #""[^"]*""

      Symboles ma-fonction java.util.List s/split

      Mots clés :nom :xml/tag ::local

       Vecteurs [4 5 6 "cueillir des cerises"]

          Maps {:key "value", 69000 "Lyon", nil -1}

           Sets #{1 "mixed" :bag}

Appel de fonction (println "Bonjour Maître !")
                   (if (test x)
    Structure de     (print "then")
        contrôle     (print "else"))
Commentaire ; ligne             #_(bloc de code)

         Chaîne "Bonjour Lyon !nBonjour Mix-IT !"    java.lang.String

                                                      java.lang.Long
       Nombres 42 2/3 3.14 1e6 12.34M 42N             java.lang.Double

     Caractères a newline space                    java.lang.Character

      Booléens true false                             java's true & false

            null nil                                  java's null

       Regexes #"a*b*"        #""[^"]*""           java.util.Pattern

      Symboles ma-fonction java.util.List s/split

      Mots clés :nom :xml/tag ::local

       Vecteurs [4 5 6 "cueillir des cerises"]        java.util.List

          Maps {:key "value", 69000 "Lyon", nil -1}   java.util.Map

           Sets #{1 "mixed" :bag}                     java.util.Set

Appel de fonction (println "Bonjour Maître !")
                   (if (test x)
    Structure de     (print "then")
        contrôle     (print "else"))
Fonctions
Fonctions "anonymes" : définitions


       Explicite :
       (fn [args] ...code...)
Fonctions "anonymes" : définitions


       Explicite :
       (fn [args] ...code...)

       Contractée 1 argument :
       #(foo (bar %) (baz %))
Fonctions "anonymes" : définitions


       Explicite :
       (fn [args] ...code...)

       Contractée 1 argument :
       #(foo (bar %) (baz %))

       Contractée n arguments :
       #(foo (bar %1) (baz %2))
Fonctions "anonymes" : définitions


                              Explicite :
                              (fn [args] ...code...)

                              Contractée 1 argument :
                              #(foo (bar %) (baz %))

                              Contractée n arguments :
                              #(foo (bar %1) (baz %2))


        Description                  Forme explicite     Forme contractée
Incrémente de 2                (fn [x] (+ x 2))        #(+ % 2)
Ajoute la taille de e à sum    (fn [sum e]             #(+ %1 (count %2))
                                 (+ sum (count e)) )
Fonctions "anonymes" : Utilisation



Exemple 1 : Ajoute 2 à tous les éléments d'une séquence

(map f s) => Transforme la séquence s en appliquant à
chaque élément x la fonction f : (f x)

; Forme explicite
user=> (map (fn [x] (+ x 2)) [1 2 3])
(3 4 5)

; Forme contractée
user=> (map #(+ % 2) [1 2 3])
(3 4 5)
Fonctions "anonymes" : Utilisation



Exemple 2 : Somme totale des tailles des éléments d'une
liste

(reduce f val s) => Calcule une valeur v de manière
itérative en calculant d'abord v0 = (f val e0) puis v1 = (f v0
e1), etc. v = (f vn-1 en)

Ou encore :
on parcourt s avec une valeur qu'on "accumule" d'un
élément à l'autre, et on retourn la valeur accumulée

user=> (reduce + 0 [1 1 1])
3
Fonctions "anonymes" : Utilisation


Exemple 2 : Somme totale des tailles des éléments d'une
liste
; Forme explicite
user=> (reduce (fn [sum e]
                    (+ sum (count e)))
                 0
                 ["a" "bc" "def"])
6
Fonctions "anonymes" : Utilisation


Exemple 2 : Somme totale des tailles des éléments d'une
liste
; Forme explicite
user=> (reduce (fn [sum e]
                    (+ sum (count e)))
                 0
                 ["a" "bc" "def"])
6

; Forme contractée
user=> (reduce #(+ %1 (count %2))
               0
               ["a" "bc" "def"])
6
Fonctions globales et constantes

  Déclarées dans un espace de noms, un "namespace"
    ~ package
  Documentation en ligne, introspectable

;; Fichier mix_it/clojure.clj
(ns mix-it.clojure)

(def add-2 (fn [x] (+ x 2)))

(defn add-2 [x] (+ x 2))

(defn add-2
  "Incrémente x de 2"
  [x]
  (+ x 2))
Structures "persistantes"

  (not= "Persistant" "stockage disque")

Valeur originale persiste après "modification"
   undo for free
   pas de doute sur le comportement des lib tierces
Données immuables
   "modification" = création d'une instance modifiée
Modifications performantes
   Pas de copie brutale, stocké en arbre
   Partage de structure (les branches inchangées)
   Opérations en O(log32(n))
   ~ O(1) pour des n réalistes
Structures "persistantes"

user=> (def v1 [:a :b :c])
#'user/v1
user=> (def v2 (assoc v1 0 :A))
#'user/v2
user=> [v1 v2]
[[:a :b :c] [:A :b :c]]
Structures "persistantes" : opérations
       génériques

Collections      count, conj, seq

                 vector, vec, get, assoc, pop,
Vectors
                 ...

                 hash-map, assoc, dissoc,
Maps
                 merge, zipmap, ...
                 hash-set, disj, union,
Sets
                 difference, intersection, ...
Associative      update-in, assoc-in
structures

              http://clojure.org/data_structures
Structures "persistantes" : exemple



user=> (def mix-it
         {:ou "Lyon",
          :stats {:participants 198,
                   :speakers 25}})
#'user/mix-it

user=> (update-in mix-it
                  [:stats :participants]
                  + 2)
{:ou "Lyon", :stats {:participants 200,
                     :speakers 25}}
Orthogonalité
Orthogonalité : (lazy) sequences

Iterators done right

Vue sequentielle d'une collection
  fonction seq => vue "naturelle"
  d'autres fonctions: rseq subseq rsubseq vals
  keys...

Immuables bien entendu
Orthogonalité : (lazy) sequences

Abstraction d'une liste liée : 3,5 fonctions
   constructeur
   (cons 1 (cons 2 nil)) => (1 2)
   first
   rest (ou next)
   user=> (let [s [1 2 3 4]]
                [(first s) (rest s) (next s)])
   [1 (2 3 4) (2 3 4)]

seq implicite
   (cons 1 [2 3]) => (1 2 3)
   (first {:a 1 :b 2 :c 3}) => [:a 1]
   (next {:a 1 :b 2 :c 3}) => [:b 2 :c 3]
Orthogonalité : (lazy) sequences
3 fonctions et demi ?
    rest vs next
    laziness

Majorité des séquences lazy
   non simultanément réalisées en mémoire
   "medium éphémère de traitement"
   pipelines !!!
   vers l'infini et au delà !
       gros volumes
       simplification des algos
Orthogonalité : lazy sequences
Clear upgrade path




                     Accès uniforme aux champs

                     Polymorphisme non intrusif
Accès uniforme aux données


Implémentation plus ou moins évoluée ...
   Donnée non typée :
(def dnt {:nom "Mix-it", :participants
                                  200})
   Donnée typée :
(defrecord Event [nom participants])
(def dt (Event. "Mix-it" 200))

... mais accès aux données uniforme côté "client" :

user=> (:nom dnt)
"Mix-it"
user=> (:participants dt)
200
Polymorphisme non intrusif

Ce matin, un lapin ...

(defn mk-lapin [couleur]
  {:espece :lapin, :couleur couleur})
(defn mk-chasseur [arme]
  {:espece :chasseur, :arme arme})

(def l1 (mk-lapin :gris))
(def l2 (mk-lapin :noir))

(def c1 (mk-chasseur :couteau))
(def c2 (mk-chasseur :fusil))
Polymorphisme non intrusif


Version 1: fonction simple, combinaison d'espèces
combinables close



   (defn croise [x y]
    (condp = [(:espece x) (:espece y)]
      [:lapin :chasseur] :fuit
      [:lapin :lapin] :accouple ;TODO
      [:chasseur :chasseur] :trinque
      [:chasseur :lapin] :tue))
Polymorphisme non intrusif

Version 2: Multiméthodes = dispatch en fonction des
arguments = "héritage simple on steroids"
(defmulti croise
  (fn [x y] [(:espece x) (:espece y)]))
(defmethod croise [:lapin :chasseur]
  [l c] :fuit)
(defmethod croise [:lapin :lapin] ;TODO
  [l1 l2] :accouple)
(defmethod croise [:chasseur :chasseur]
  [c1 c2] :trinque)
(defmethod croise [:chasseur :lapin]
  [c l] :tue)
Polymorphisme non intrusif

          Fait remarquable :
 Code appelant identique dans les 2 cas


     user=> (croise     l1 l2)
     :accouple
     user=> (croise     c1 c2)
     :trinque
     user=> (croise     c1 l1)
     :tue
     user=> (croise     l1 c1)
     :fuit
Polymorphisme non intrusif

         Fait remarquable 2 :
multiméthodes extensibles et redefinables
introduire :lapine héritant de :lapin
redéfinir croise pour [:lapin :lapin]
 définir croise pour [:lapin :lapine]
Gestion des états
Mutable stateful objects are the
new spaghetti code:
   Hard to understand, test,
   reason about
   Concurrency disaster
Concurrent
  threadsafe
    managé
    GC-like
Gestion saine des états


Pas que pour le multithread
Etat : ensemble des valeurs prises par toutes les
variables d'un système à un instant donné
    trop de variables en OO classique
    difficulté à raisonner sur le système
Confusion entre :
    valeur
    identité
Confusion valeur identité



Qui n'a jamais douté d'une lib tierce ?
   A-t-elle garder une référence sur mon objet ?
   Cet objet est-il la valeur présente ou est-il mis à jour
   en continu ?
Ruine la programmation par valeurs
Confusion valeur identité

Map container = new HashMap();
Map a = new HashMap() {
   {this.put("a", 1); this.put("b", 2);}
};
Map b = new HashMap() {
 {this.put("a", 1); this.put("b", 3);}
};
container.put(a, "bientôt introuvable");
a.put("b", 3); //
System.out.println(container.get(a));
// null
System.out.println(container.get(b));
// null
System.out.println(container);
// {{b=3, a=1}=bientôt inaccessible}
Confusion valeur identité



Question clé :
Cet objet est-il la valeur présente ou est-il mis à jour
par ailleurs ?
Pas de confusion sur les primitives
   car immuables
Cas d'école : JodaTime vs j.u.Calendar
   immuable = tranquilité d'esprit
   immuable = threadsafe
Incrédules ? Lisez JCIP !
Confusion valeur/identité


Faut que ça change !
   Tout est immuable
   Sauf les références
Une référence est juste une boîte
   contient une valeur
   dont le contenu peut changer
Aucune ambiguité
   Soit c'est une identité (référence)
   Soit c'est une valeur
   Code plus clair
   Moins de code défensif
   (je vais me faire une copie au cas où)
Le cadeau Bonux


Références en tant qu'objets
   first class
        passables en paramètre
        ou valeurs de retour
        etc.
   peuvent imposer une logique
Déjà vu ?
   java.lang.ref.* pardi !
Le cadeau Bonux

java.lang.ref.*
   ajoute une logique spécifique pour la
   gestion mémoire
Références Clojure
   ajoutent une logique spécifique pour la
   gestion de la concurrence !
Plusieurs types de références
   les refs, pour les màj transactionnelles
   les atoms, pour les màj isolées
   les agents, pour les màj isolées et asynchrones
   chaque type est un pattern de coordination
Le cadeau Bonux


  Modèle unifié
    @ ou deref pour lire
    toujours la même signature pour les
    fns de mise à jour

(def une-ref (ref 39))
(def un-agent (agent 21))
(def un-atom (atom 63))

[@une-ref @un-agent @un-atom]
; [39 21 63]

(alter une-ref + 3)
(send un-agent * 2)
(swap! un-atom * 2/3)
Y en a un
peu plus, je
vous le mets
quand même ?
Pour en savoir plus

clojure.org
disclojure.org – excellent daily digest
quelques librairies : ring, incanter, compojure, enlive
le channel irc #clojure et le google group
les multiples livres :
    Programming Clojure (daté)
    Practical Clojure
    Joy of Clojure (érudit)
    et Clojure Programming (bientôt en rough cut)
    d'autres en préparation : Programming Clojure 2nd
    ed, Clojure in Action, Meeting Clojure etc.
Questions
(let [prejuge
        (= LISP "Lots of Irritating
                 Superfluous Parentheses")]
  (not= Clojure prejuge))


                 VRAIMENT ?
                 Voyez plutôt ...
(let [prejuge
        (= LISP "Lots of Irritating
                 Superfluous Parentheses")]
  (not= Clojure prejuge))

Java :
obj.getClient().getAdresse().getZipCode() => ()()()

Clojure :
(-> obj .getClient .getAdresse .getZipCode) => ()
(let [prejuge
        (= LISP "Lots of Irritating
                 Superfluous Parentheses")]
  (not= Clojure prejuge))

Java :
if ( nullable != null ) {
    ... foo ...
} else {
    ... bar ...
} => () {} {}

Clojure :
(if nullable
  ... foo ...
  ... bar ...) => ()
Orienté Objet




   Nous pouvons le reconstruire,
Nous en avons la possibilité technique

Contenu connexe

Tendances

Requêtes HTTP synchrones et asynchrones
Requêtes HTTPsynchrones et asynchronesRequêtes HTTPsynchrones et asynchrones
Requêtes HTTP synchrones et asynchronesAbdoulaye Dieng
 
Introduction à React JS
Introduction à React JSIntroduction à React JS
Introduction à React JSAbdoulaye Dieng
 
Génération automatique de texte
Génération automatique de texteGénération automatique de texte
Génération automatique de texteEstelle Delpech
 
Notions de base de JavaScript
Notions de base de JavaScriptNotions de base de JavaScript
Notions de base de JavaScriptKristen Le Liboux
 
Introduction à JavaScript
Introduction à JavaScriptIntroduction à JavaScript
Introduction à JavaScriptAbdoulaye Dieng
 
.php1 : les fondamentaux du PHP
.php1 : les fondamentaux du PHP.php1 : les fondamentaux du PHP
.php1 : les fondamentaux du PHPAbdoulaye Dieng
 
Exports de r vers office
Exports de r vers officeExports de r vers office
Exports de r vers officefrancoismarical
 
Javascript pour les Développeurs WEB
Javascript pour les Développeurs WEBJavascript pour les Développeurs WEB
Javascript pour les Développeurs WEBAbbes Rharrab
 
Monitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et PinbaMonitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et PinbaPatrick Allaert
 
Nouveautés JavaScript dans le monde Microsoft
Nouveautés JavaScript dans le monde MicrosoftNouveautés JavaScript dans le monde Microsoft
Nouveautés JavaScript dans le monde Microsoftdavrous
 
Programmation orientée objet en PHP 5
Programmation orientée objet en PHP 5Programmation orientée objet en PHP 5
Programmation orientée objet en PHP 5Kristen Le Liboux
 
Tout ce que le getting started mongodb ne vous dira pas
Tout ce que le getting started mongodb ne vous dira pasTout ce que le getting started mongodb ne vous dira pas
Tout ce que le getting started mongodb ne vous dira pasBruno Bonnin
 

Tendances (15)

Requêtes HTTP synchrones et asynchrones
Requêtes HTTPsynchrones et asynchronesRequêtes HTTPsynchrones et asynchrones
Requêtes HTTP synchrones et asynchrones
 
Introduction à React JS
Introduction à React JSIntroduction à React JS
Introduction à React JS
 
Génération automatique de texte
Génération automatique de texteGénération automatique de texte
Génération automatique de texte
 
Spark dataframe
Spark dataframeSpark dataframe
Spark dataframe
 
Notions de base de JavaScript
Notions de base de JavaScriptNotions de base de JavaScript
Notions de base de JavaScript
 
Introduction à JavaScript
Introduction à JavaScriptIntroduction à JavaScript
Introduction à JavaScript
 
Cours php
Cours phpCours php
Cours php
 
.php1 : les fondamentaux du PHP
.php1 : les fondamentaux du PHP.php1 : les fondamentaux du PHP
.php1 : les fondamentaux du PHP
 
Exports de r vers office
Exports de r vers officeExports de r vers office
Exports de r vers office
 
Javascript pour les Développeurs WEB
Javascript pour les Développeurs WEBJavascript pour les Développeurs WEB
Javascript pour les Développeurs WEB
 
Monitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et PinbaMonitoring d'applications/environnements PHP: APM et Pinba
Monitoring d'applications/environnements PHP: APM et Pinba
 
Nouveautés JavaScript dans le monde Microsoft
Nouveautés JavaScript dans le monde MicrosoftNouveautés JavaScript dans le monde Microsoft
Nouveautés JavaScript dans le monde Microsoft
 
Introduction à jQuery
Introduction à jQueryIntroduction à jQuery
Introduction à jQuery
 
Programmation orientée objet en PHP 5
Programmation orientée objet en PHP 5Programmation orientée objet en PHP 5
Programmation orientée objet en PHP 5
 
Tout ce que le getting started mongodb ne vous dira pas
Tout ce que le getting started mongodb ne vous dira pasTout ce que le getting started mongodb ne vous dira pas
Tout ce que le getting started mongodb ne vous dira pas
 

En vedette

HTML5... La révolution maintenant!
HTML5... La révolution maintenant!HTML5... La révolution maintenant!
HTML5... La révolution maintenant!CARA_Lyon
 
Comment j'ai mis ma suite de tests au régime en 5 minutes par jour
Comment j'ai mis ma suite de tests au régime en 5 minutes par jourComment j'ai mis ma suite de tests au régime en 5 minutes par jour
Comment j'ai mis ma suite de tests au régime en 5 minutes par jourCARA_Lyon
 
Sur la route de l'agilité @Mix-it Lyon 2011
Sur la route de l'agilité @Mix-it Lyon 2011Sur la route de l'agilité @Mix-it Lyon 2011
Sur la route de l'agilité @Mix-it Lyon 2011Mickael Istria
 
Mix-IT (Apr 2011) - Intelligence Collective avec Apache Mahout
Mix-IT (Apr 2011) - Intelligence Collective avec Apache MahoutMix-IT (Apr 2011) - Intelligence Collective avec Apache Mahout
Mix-IT (Apr 2011) - Intelligence Collective avec Apache MahoutMichaël Figuière
 
Building complex and modular RIAs with OSGi and Flex
Building complex and modular RIAs with OSGi and FlexBuilding complex and modular RIAs with OSGi and Flex
Building complex and modular RIAs with OSGi and FlexCARA_Lyon
 
Agile Sans Frontières
Agile Sans FrontièresAgile Sans Frontières
Agile Sans FrontièresCARA_Lyon
 

En vedette (6)

HTML5... La révolution maintenant!
HTML5... La révolution maintenant!HTML5... La révolution maintenant!
HTML5... La révolution maintenant!
 
Comment j'ai mis ma suite de tests au régime en 5 minutes par jour
Comment j'ai mis ma suite de tests au régime en 5 minutes par jourComment j'ai mis ma suite de tests au régime en 5 minutes par jour
Comment j'ai mis ma suite de tests au régime en 5 minutes par jour
 
Sur la route de l'agilité @Mix-it Lyon 2011
Sur la route de l'agilité @Mix-it Lyon 2011Sur la route de l'agilité @Mix-it Lyon 2011
Sur la route de l'agilité @Mix-it Lyon 2011
 
Mix-IT (Apr 2011) - Intelligence Collective avec Apache Mahout
Mix-IT (Apr 2011) - Intelligence Collective avec Apache MahoutMix-IT (Apr 2011) - Intelligence Collective avec Apache Mahout
Mix-IT (Apr 2011) - Intelligence Collective avec Apache Mahout
 
Building complex and modular RIAs with OSGi and Flex
Building complex and modular RIAs with OSGi and FlexBuilding complex and modular RIAs with OSGi and Flex
Building complex and modular RIAs with OSGi and Flex
 
Agile Sans Frontières
Agile Sans FrontièresAgile Sans Frontières
Agile Sans Frontières
 

Similaire à Mix it 2011 - Clojure

Patrons de conception de la programmation fonctionnelle
Patrons de conception de la programmation fonctionnellePatrons de conception de la programmation fonctionnelle
Patrons de conception de la programmation fonctionnelleAssociation Agile Nantes
 
De java à swift en 2 temps trois mouvements
De java à swift en 2 temps trois mouvementsDe java à swift en 2 temps trois mouvements
De java à swift en 2 temps trois mouvementsDidier Plaindoux
 
Programmation fonctionnelle
Programmation fonctionnelleProgrammation fonctionnelle
Programmation fonctionnelleJean Detoeuf
 
Introduction à scala
Introduction à scalaIntroduction à scala
Introduction à scalaSOAT
 
Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#
Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#
Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#MSDEVMTL
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation Groovyguest6e3bed
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation GroovyJS Bournival
 
Test unitaires - refactoring - clean code
Test unitaires - refactoring - clean codeTest unitaires - refactoring - clean code
Test unitaires - refactoring - clean codeHadrien Blanc
 
Qualité logicielle
Qualité logicielleQualité logicielle
Qualité logiciellecyrilgandon
 
Scripting Linux avec Python partie1.pptx
Scripting Linux avec Python partie1.pptxScripting Linux avec Python partie1.pptx
Scripting Linux avec Python partie1.pptxFrancisRUKAN
 
Formation java script
Formation java scriptFormation java script
Formation java scriptRomdhani Asma
 
Seance_1_cours_introduction_java_Copie.pptx
Seance_1_cours_introduction_java_Copie.pptxSeance_1_cours_introduction_java_Copie.pptx
Seance_1_cours_introduction_java_Copie.pptxRihabBENLAMINE
 
cour de compilation
cour de compilation cour de compilation
cour de compilation Ens Kouba
 
Enib cours c.a.i. web - séance #5 : scala play! framework
Enib   cours c.a.i. web - séance #5 : scala play! frameworkEnib   cours c.a.i. web - séance #5 : scala play! framework
Enib cours c.a.i. web - séance #5 : scala play! frameworkHoracio Gonzalez
 
Exploiter php 5
Exploiter php 5Exploiter php 5
Exploiter php 5halleck45
 
Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5Mamadou Oury Ba
 

Similaire à Mix it 2011 - Clojure (20)

Patrons de conception de la programmation fonctionnelle
Patrons de conception de la programmation fonctionnellePatrons de conception de la programmation fonctionnelle
Patrons de conception de la programmation fonctionnelle
 
Drools
DroolsDrools
Drools
 
De java à swift en 2 temps trois mouvements
De java à swift en 2 temps trois mouvementsDe java à swift en 2 temps trois mouvements
De java à swift en 2 temps trois mouvements
 
Programmation fonctionnelle
Programmation fonctionnelleProgrammation fonctionnelle
Programmation fonctionnelle
 
Langage Perl
Langage PerlLangage Perl
Langage Perl
 
Introduction à scala
Introduction à scalaIntroduction à scala
Introduction à scala
 
Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#
Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#
Kevin Olivier Avignon: Une introduction à la pensée fonctionnelle avec F#
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation Groovy
 
Présentation Groovy
Présentation GroovyPrésentation Groovy
Présentation Groovy
 
Test unitaires - refactoring - clean code
Test unitaires - refactoring - clean codeTest unitaires - refactoring - clean code
Test unitaires - refactoring - clean code
 
Qualité logicielle
Qualité logicielleQualité logicielle
Qualité logicielle
 
Tp l3
Tp l3Tp l3
Tp l3
 
Scripting Linux avec Python partie1.pptx
Scripting Linux avec Python partie1.pptxScripting Linux avec Python partie1.pptx
Scripting Linux avec Python partie1.pptx
 
Formation java script
Formation java scriptFormation java script
Formation java script
 
Seance_1_cours_introduction_java_Copie.pptx
Seance_1_cours_introduction_java_Copie.pptxSeance_1_cours_introduction_java_Copie.pptx
Seance_1_cours_introduction_java_Copie.pptx
 
cour de compilation
cour de compilation cour de compilation
cour de compilation
 
Builsing DSL using MDE
Builsing DSL using MDEBuilsing DSL using MDE
Builsing DSL using MDE
 
Enib cours c.a.i. web - séance #5 : scala play! framework
Enib   cours c.a.i. web - séance #5 : scala play! frameworkEnib   cours c.a.i. web - séance #5 : scala play! framework
Enib cours c.a.i. web - séance #5 : scala play! framework
 
Exploiter php 5
Exploiter php 5Exploiter php 5
Exploiter php 5
 
Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5
 

Mix it 2011 - Clojure

  • 1. Clojure (oubliez vos préjugés) Christophe Grand @cgrand Laurent Petit @petitlaurent Mix-it 2011, 5 avril
  • 4. Réduire la complexité accidentelle "Out of the tar pit" ( http://ben.moseley.name/frp/paper-v1_01.pdf ) Distinguer la complexité inhérente de l'accidentelle "Le mythe du mois-homme" ( Frederick P. Brooks, Jr.) Ch. 16: "Pas de balle d'argent: l'essence et la substance en génie logiciel"
  • 5. Réduire la complexité accidentelle "Out of the tar pit" ( http://ben.moseley.name/frp/paper-v1_01.pdf ) Distinguer la complexité inhérente de l'accidentelle "Le mythe du mois-homme" ( Frederick P. Brooks, Jr.) Ch. 16: "Pas de balle d'argent: l'essence et la substance en génie logiciel" "Comme Aristote, je divise [les difficultés de la technologie logicielle] en essence - celles qui sont inhérentes à la nature du logiciel - et en substance - celles qui gênent aujourd'hui sa production, mais n'y sont pas inhérentes." (F. P. Brooks dans Le mythe du mois-homme)
  • 6. Réduire la complexité accidentelle Simple fiable orthogonal peu de syntaxe peu de concepts peu de surprises larges abstractions
  • 8. Programmation concurrente Mécanismes de bas niveau (lock, synchronized) OOP introduit de la complexité Myriades de petits états répartis très difficiles à manipuler de manière cohérente "#antioopargs OO, as practiced e.g. in Java, conflates identity, state and behavior" @stilkov, expert java reconnu
  • 9. Motivations Réduire complexité accidentelle Programmation concurrente Expressivité maximale
  • 10. Expressivité maximale du langage Concision Le moins "cérémonieux possible" ! Factorisation Oser passer aux génériques ! Adaptation "Si le problème ne vient pas au langage, le langage ira au problème" ! Degrés d'abstraction Rester dans le langage, en bonne compagnie !
  • 11. Expressivité maximale du langage Approche combinée Top Down et Bottom Up Décomposition fonctionnelle .... (top down) Mais construction d'un "langage" pour le domaine du problème (bottom up) Equivalent des APIs dites "fluent" en java /* exemple d'API "fluent" dans JPA */ em.createNamedQuery("Student.findByNameAgeGender") .setParameter("name", name) .setHint("hintName", "hintValue") .getResultList(); = mini-langages "embarqués" (DSLs)
  • 12. Expressivité maximale du langage Encore 1 fois, l'OOP par défaut est dans le collimateur ! Plus difficile de généraliser les algorithmes Plus cérémonieux Mais ne pas jeter le bébé avec l'eau du bain "Composition, interfaces, héritage : 2 bonnes idées sur 3, pourquoi tt le monde est parti avec la 3ème ?" "Vu sur Twitter" (#antioop)
  • 13. Motivations Réduire complexité accidentelle Programmation concurrente Expressivité maximale Langage généraliste
  • 14. Langage généraliste Plateforme industrielle interopérable performant déployable réutilisation de l'existant Pas un langage de niche performant dans le cas général sans perdre les qualités du langage (pas de contorsions)
  • 15. Motivations Réduire complexité accidentelle Programmation concurrente Expressivité maximale Langage généraliste
  • 16. Motivations Réduire complexité accidentelle Programmation concurrente Expressivité maximale Langage généraliste
  • 17. Motivations Réduire complexité accidentelle Programmation concurrente Expressivité maximale Langage généraliste
  • 20. Base fonctionnelle "It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." - Alan J. Perlis Briques de base simples Fonctions Structures de données génériques Fonctions sans effet de bord Sur leurs arguments Sur leur environnement Retournant une valeur non altérable => Naturellement thread-safe !
  • 21. Base fonctionnelle Sans hypothéquer le pouvoir d'abstraction/indirection Fonctions en paramètres Fonctions en valeur de retour ("closures") Fonctions polymorphes Application : le concept de "séquence" en Clojure: "It is better to have 100 functions operate on one data abstraction than 10 functions on 10 data structures" - Attributed to Rich Hickey
  • 23. Etats managés La base fonctionnelle seule est insuffisante Besoin d'orchestrer la mutation des états Pour écrire des programmes concurrents fiables Mais aussi pour bien organiser son code ! Confiner la mutation des états comme on confine le traitement des entrées-sorties ! On installe cette orchestration au coeur du langage = idiomatique et orthogonal
  • 24. Etats managés Séparation stricte identité / valeur Identité = stable au cours du temps Etat = Valeur des caractéristiques d'une identité à un instant t Valeur = ensemble immuable de caractéristiques Le temps passe, les identités sont stables, leurs valeurs changent PRIMITIVES DE HAUT NIVEAU POUR GERER LES TRANSITIONS D'ETAT
  • 26. LISP Syntaxe simple et uniforme Rapproche donnée et code "Data is code, and code is data" En utilisant les macros à la compilation : "code writing code" Alternative à la répétition de certains patterns Possibilité de rester dans le langage plus longtemps (pas de génération de code depuis UML !) Suppression possible des derniers "boilerplates" du code
  • 27. LISP Dynamique REPL = "Read, Eval, Print, Loop" Le code est évalué (en fait compilé) ligne à ligne On peut continuer d'évaluer du nouveau code au runtime On peut recharger les valeurs des fonctions/variables à chaud On peut évaluer n'importe quel code de test/initialisation à chaud Très pratique pour le prototypage, le debug .... Un langage "AGILE" !
  • 29. JVM JVM pragmatisme Compilé écosystème interactif ou AOT entreprise *warn-on-reflection* Performant Java en benchmark Hotspot-friendly
  • 30. JVM Interop Zéro overhead java.util.* Runnable & Callable
  • 33. Commentaire ; ligne #_(bloc de code) Chaîne "Bonjour Lyon !nBonjour Mix-IT !" Nombres 42 2/3 3.14 1e6 12.34M 42N Caractères a newline space Booléens true false null nil Regexes #"a*b*" #""[^"]*"" Symboles ma-fonction java.util.List s/split Mots clés :nom :xml/tag ::local Vecteurs [4 5 6 "cueillir des cerises"] Maps {:key "value", 69000 "Lyon", nil -1} Sets #{1 "mixed" :bag} Appel de fonction (println "Bonjour Maître !") (if (test x) Structure de (print "then") contrôle (print "else"))
  • 34. Commentaire ; ligne #_(bloc de code) Chaîne "Bonjour Lyon !nBonjour Mix-IT !" java.lang.String java.lang.Long Nombres 42 2/3 3.14 1e6 12.34M 42N java.lang.Double Caractères a newline space java.lang.Character Booléens true false java's true & false null nil java's null Regexes #"a*b*" #""[^"]*"" java.util.Pattern Symboles ma-fonction java.util.List s/split Mots clés :nom :xml/tag ::local Vecteurs [4 5 6 "cueillir des cerises"] java.util.List Maps {:key "value", 69000 "Lyon", nil -1} java.util.Map Sets #{1 "mixed" :bag} java.util.Set Appel de fonction (println "Bonjour Maître !") (if (test x) Structure de (print "then") contrôle (print "else"))
  • 36. Fonctions "anonymes" : définitions Explicite : (fn [args] ...code...)
  • 37. Fonctions "anonymes" : définitions Explicite : (fn [args] ...code...) Contractée 1 argument : #(foo (bar %) (baz %))
  • 38. Fonctions "anonymes" : définitions Explicite : (fn [args] ...code...) Contractée 1 argument : #(foo (bar %) (baz %)) Contractée n arguments : #(foo (bar %1) (baz %2))
  • 39. Fonctions "anonymes" : définitions Explicite : (fn [args] ...code...) Contractée 1 argument : #(foo (bar %) (baz %)) Contractée n arguments : #(foo (bar %1) (baz %2)) Description Forme explicite Forme contractée Incrémente de 2 (fn [x] (+ x 2)) #(+ % 2) Ajoute la taille de e à sum (fn [sum e] #(+ %1 (count %2)) (+ sum (count e)) )
  • 40. Fonctions "anonymes" : Utilisation Exemple 1 : Ajoute 2 à tous les éléments d'une séquence (map f s) => Transforme la séquence s en appliquant à chaque élément x la fonction f : (f x) ; Forme explicite user=> (map (fn [x] (+ x 2)) [1 2 3]) (3 4 5) ; Forme contractée user=> (map #(+ % 2) [1 2 3]) (3 4 5)
  • 41. Fonctions "anonymes" : Utilisation Exemple 2 : Somme totale des tailles des éléments d'une liste (reduce f val s) => Calcule une valeur v de manière itérative en calculant d'abord v0 = (f val e0) puis v1 = (f v0 e1), etc. v = (f vn-1 en) Ou encore : on parcourt s avec une valeur qu'on "accumule" d'un élément à l'autre, et on retourn la valeur accumulée user=> (reduce + 0 [1 1 1]) 3
  • 42. Fonctions "anonymes" : Utilisation Exemple 2 : Somme totale des tailles des éléments d'une liste ; Forme explicite user=> (reduce (fn [sum e] (+ sum (count e))) 0 ["a" "bc" "def"]) 6
  • 43. Fonctions "anonymes" : Utilisation Exemple 2 : Somme totale des tailles des éléments d'une liste ; Forme explicite user=> (reduce (fn [sum e] (+ sum (count e))) 0 ["a" "bc" "def"]) 6 ; Forme contractée user=> (reduce #(+ %1 (count %2)) 0 ["a" "bc" "def"]) 6
  • 44. Fonctions globales et constantes Déclarées dans un espace de noms, un "namespace" ~ package Documentation en ligne, introspectable ;; Fichier mix_it/clojure.clj (ns mix-it.clojure) (def add-2 (fn [x] (+ x 2))) (defn add-2 [x] (+ x 2)) (defn add-2 "Incrémente x de 2" [x] (+ x 2))
  • 45. Structures "persistantes" (not= "Persistant" "stockage disque") Valeur originale persiste après "modification" undo for free pas de doute sur le comportement des lib tierces Données immuables "modification" = création d'une instance modifiée Modifications performantes Pas de copie brutale, stocké en arbre Partage de structure (les branches inchangées) Opérations en O(log32(n)) ~ O(1) pour des n réalistes
  • 46. Structures "persistantes" user=> (def v1 [:a :b :c]) #'user/v1 user=> (def v2 (assoc v1 0 :A)) #'user/v2 user=> [v1 v2] [[:a :b :c] [:A :b :c]]
  • 47. Structures "persistantes" : opérations génériques Collections count, conj, seq vector, vec, get, assoc, pop, Vectors ... hash-map, assoc, dissoc, Maps merge, zipmap, ... hash-set, disj, union, Sets difference, intersection, ... Associative update-in, assoc-in structures http://clojure.org/data_structures
  • 48. Structures "persistantes" : exemple user=> (def mix-it {:ou "Lyon", :stats {:participants 198, :speakers 25}}) #'user/mix-it user=> (update-in mix-it [:stats :participants] + 2) {:ou "Lyon", :stats {:participants 200, :speakers 25}}
  • 50. Orthogonalité : (lazy) sequences Iterators done right Vue sequentielle d'une collection fonction seq => vue "naturelle" d'autres fonctions: rseq subseq rsubseq vals keys... Immuables bien entendu
  • 51. Orthogonalité : (lazy) sequences Abstraction d'une liste liée : 3,5 fonctions constructeur (cons 1 (cons 2 nil)) => (1 2) first rest (ou next) user=> (let [s [1 2 3 4]] [(first s) (rest s) (next s)]) [1 (2 3 4) (2 3 4)] seq implicite (cons 1 [2 3]) => (1 2 3) (first {:a 1 :b 2 :c 3}) => [:a 1] (next {:a 1 :b 2 :c 3}) => [:b 2 :c 3]
  • 52. Orthogonalité : (lazy) sequences 3 fonctions et demi ? rest vs next laziness Majorité des séquences lazy non simultanément réalisées en mémoire "medium éphémère de traitement" pipelines !!! vers l'infini et au delà ! gros volumes simplification des algos
  • 54. Clear upgrade path Accès uniforme aux champs Polymorphisme non intrusif
  • 55. Accès uniforme aux données Implémentation plus ou moins évoluée ... Donnée non typée : (def dnt {:nom "Mix-it", :participants 200}) Donnée typée : (defrecord Event [nom participants]) (def dt (Event. "Mix-it" 200)) ... mais accès aux données uniforme côté "client" : user=> (:nom dnt) "Mix-it" user=> (:participants dt) 200
  • 56. Polymorphisme non intrusif Ce matin, un lapin ... (defn mk-lapin [couleur] {:espece :lapin, :couleur couleur}) (defn mk-chasseur [arme] {:espece :chasseur, :arme arme}) (def l1 (mk-lapin :gris)) (def l2 (mk-lapin :noir)) (def c1 (mk-chasseur :couteau)) (def c2 (mk-chasseur :fusil))
  • 57. Polymorphisme non intrusif Version 1: fonction simple, combinaison d'espèces combinables close (defn croise [x y] (condp = [(:espece x) (:espece y)] [:lapin :chasseur] :fuit [:lapin :lapin] :accouple ;TODO [:chasseur :chasseur] :trinque [:chasseur :lapin] :tue))
  • 58. Polymorphisme non intrusif Version 2: Multiméthodes = dispatch en fonction des arguments = "héritage simple on steroids" (defmulti croise (fn [x y] [(:espece x) (:espece y)])) (defmethod croise [:lapin :chasseur] [l c] :fuit) (defmethod croise [:lapin :lapin] ;TODO [l1 l2] :accouple) (defmethod croise [:chasseur :chasseur] [c1 c2] :trinque) (defmethod croise [:chasseur :lapin] [c l] :tue)
  • 59. Polymorphisme non intrusif Fait remarquable : Code appelant identique dans les 2 cas user=> (croise l1 l2) :accouple user=> (croise c1 c2) :trinque user=> (croise c1 l1) :tue user=> (croise l1 c1) :fuit
  • 60. Polymorphisme non intrusif Fait remarquable 2 : multiméthodes extensibles et redefinables introduire :lapine héritant de :lapin redéfinir croise pour [:lapin :lapin] définir croise pour [:lapin :lapine]
  • 62.
  • 63. Mutable stateful objects are the new spaghetti code: Hard to understand, test, reason about Concurrency disaster
  • 64. Concurrent threadsafe managé GC-like
  • 65. Gestion saine des états Pas que pour le multithread Etat : ensemble des valeurs prises par toutes les variables d'un système à un instant donné trop de variables en OO classique difficulté à raisonner sur le système Confusion entre : valeur identité
  • 66. Confusion valeur identité Qui n'a jamais douté d'une lib tierce ? A-t-elle garder une référence sur mon objet ? Cet objet est-il la valeur présente ou est-il mis à jour en continu ? Ruine la programmation par valeurs
  • 67. Confusion valeur identité Map container = new HashMap(); Map a = new HashMap() { {this.put("a", 1); this.put("b", 2);} }; Map b = new HashMap() { {this.put("a", 1); this.put("b", 3);} }; container.put(a, "bientôt introuvable"); a.put("b", 3); // System.out.println(container.get(a)); // null System.out.println(container.get(b)); // null System.out.println(container); // {{b=3, a=1}=bientôt inaccessible}
  • 68. Confusion valeur identité Question clé : Cet objet est-il la valeur présente ou est-il mis à jour par ailleurs ? Pas de confusion sur les primitives car immuables Cas d'école : JodaTime vs j.u.Calendar immuable = tranquilité d'esprit immuable = threadsafe Incrédules ? Lisez JCIP !
  • 69. Confusion valeur/identité Faut que ça change ! Tout est immuable Sauf les références Une référence est juste une boîte contient une valeur dont le contenu peut changer Aucune ambiguité Soit c'est une identité (référence) Soit c'est une valeur Code plus clair Moins de code défensif (je vais me faire une copie au cas où)
  • 70. Le cadeau Bonux Références en tant qu'objets first class passables en paramètre ou valeurs de retour etc. peuvent imposer une logique Déjà vu ? java.lang.ref.* pardi !
  • 71. Le cadeau Bonux java.lang.ref.* ajoute une logique spécifique pour la gestion mémoire Références Clojure ajoutent une logique spécifique pour la gestion de la concurrence ! Plusieurs types de références les refs, pour les màj transactionnelles les atoms, pour les màj isolées les agents, pour les màj isolées et asynchrones chaque type est un pattern de coordination
  • 72. Le cadeau Bonux Modèle unifié @ ou deref pour lire toujours la même signature pour les fns de mise à jour (def une-ref (ref 39)) (def un-agent (agent 21)) (def un-atom (atom 63)) [@une-ref @un-agent @un-atom] ; [39 21 63] (alter une-ref + 3) (send un-agent * 2) (swap! un-atom * 2/3)
  • 73. Y en a un peu plus, je vous le mets quand même ?
  • 74. Pour en savoir plus clojure.org disclojure.org – excellent daily digest quelques librairies : ring, incanter, compojure, enlive le channel irc #clojure et le google group les multiples livres : Programming Clojure (daté) Practical Clojure Joy of Clojure (érudit) et Clojure Programming (bientôt en rough cut) d'autres en préparation : Programming Clojure 2nd ed, Clojure in Action, Meeting Clojure etc.
  • 76. (let [prejuge (= LISP "Lots of Irritating Superfluous Parentheses")] (not= Clojure prejuge)) VRAIMENT ? Voyez plutôt ...
  • 77. (let [prejuge (= LISP "Lots of Irritating Superfluous Parentheses")] (not= Clojure prejuge)) Java : obj.getClient().getAdresse().getZipCode() => ()()() Clojure : (-> obj .getClient .getAdresse .getZipCode) => ()
  • 78. (let [prejuge (= LISP "Lots of Irritating Superfluous Parentheses")] (not= Clojure prejuge)) Java : if ( nullable != null ) { ... foo ... } else { ... bar ... } => () {} {} Clojure : (if nullable ... foo ... ... bar ...) => ()
  • 79. Orienté Objet Nous pouvons le reconstruire, Nous en avons la possibilité technique