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
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)
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)
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" !
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
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
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]
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.