39
@benoit_lemoine #monoid9000 La puissance de mon monoïde est supérieure à 9000 @benoit_lemoine Xebia Développeur

Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

Embed Size (px)

Citation preview

Page 1: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

La puissance de mon monoïde est supérieure à 9000

@benoit_lemoine Xebia Développeur

Page 2: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Qu’est ce qu’un monoïde ?

Un monoïde est un magma unifère associatif

Non, en vrai, c’est quoi un monoïde ?

Page 3: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

TypeScript • Syntaxe proche de JavaScript • Sucre syntaxique et classe • Typage statique • Langage cool

Page 4: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Il était une fois…

Page 5: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

class Guy { ! constructor(public power:number) {} !! //Égalité structurelle ! equals(g:Guy): boolean { ! return g.power == this.power! } ! } !! var boo = new Guy(8500) ! console.log(boo.power) //affiche 8500 !

Modélisons Boo

Page 6: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Mais la Terre est fournie avec son protecteur

Page 7: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

var boo = new Guy(8500); ! var goku = new Guy(4000); !! function fight(guy1:Guy, guy2:Guy):Guy { ! return guy1.power > guy2.power ? guy1 : guy2; ! }; !! var winner = fight(goku, boo); ! console.log(winner); ! // affiche boo, car il est de loin le plus fort !!

Modélisons Goku et le combat

Page 8: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Goku a perdu !

Page 9: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Appel à un ami !

Page 10: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

var boo = new Guy(8500); !var goku = new Guy(4000); !var vegeta = new Guy(3900); !!function fusion(guy1:Guy, guy2:Guy):Guy { ! return new Guy(guy1.power + guy2.power); !} !!var bejito = fusion(goku, vegeta); // puissance de 7900 !!var winner = fight(bejito, boo) !console.log(winner) !// affiche toujours boo, car il est toujours le plus fort !

Modélisons la fusion

Page 11: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Bejito est vaincu !

Page 12: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Augmentation du nombre de ressources

Page 13: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

var goten = new Guy(2000); ! var trunks = new Guy(2500); !! var gotenks = fusion(goten, trunks); // puissance de 4500 ! var gobejitenks = fusion(gotenks, bejito); // puissance de 12400 !! fight(gobejitenks, boo); // gobejitenks gagne !

Fusion partout

Page 14: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

La victoire était assurée car

Page 15: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

function fusion(guy1:Guy, guy2:Guy):Guy { ! return new Guy(guy1.power + guy2.power); ! } !

Loi de composition interne

fusion est une fonction… •  …qui prend 2 paramètres du même type Guy !•  …qui renvoie une valeur du même type Guy !

Page 16: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

function fusion(guy1:Guy, guy2:Guy):Guy { ! return new Guy(guy1.power + guy2.power); ! } !! var fusionned1 = fusion(fusion(goku, vegeta), goten); ! var fusionned2 = fusion(goku, fusion(vegeta, goten)); !! console.log(fusionned1.equals(fusionned2)); //affiche true!

Associativité

Page 17: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

var gobejitenks = [goku, vegeta, goten, trunks].reduce(fusion) !

Loi de composition interne associative

Rend plus lisible les fold/inject/reduce!

Page 18: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

var goku = new Guy(4000); ! var mrSatan = new Guy(0); !! var gotan = fusion(goku, mrSatan); ! var saku = fusion(mrSatan, goku); !! console.log(gotan.equals(saku)) //affiche true! console.log(gotan.equals(goku)) //affiche true! console.log(saku.equals(goku)) //affiche true!

Élément neutre de fusion!

Page 19: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

On a un monoïde ! Un monoïde est un ensemble muni d’une loi de composition interne associative et d’un élément neutre • Ensemble des Guy !• fusion !• new Guy(0) !

Page 20: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

ET ?

À quoi ça sert ?

Page 21: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

La fusion, ça prend du temps

Page 22: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Q, Librairie de promesses • Exécution séquentielle de code asynchrone • Composabilité • Les promesses, c’est cool

Page 23: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

//La fusion éxecutera la callback de promesse après 0.5 seconde ! function fusion(guy1:Guy,guy2:Guy):Q.IPromise<Guy> { ! var deferred = Q.defer(); !! setTimeout(function() { ! deferred.resolve(new Guy(guy1.power + guy2.power)) ! }, 500); !! return deferred.promise; ! }; !!

fusion différée !

Page 24: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

var boo = new Guy(8500); ! var goku = new Guy(4000); ! var vegeta = new Guy(3500); !! var promiseForBejito:Q.IPromise<Guy> = fusion(goku, vegeta); !! promiseForBejito.done(function(bejito:Guy) { ! // affiche boo, une fois que la fusion est finie ! console.log(fight(bejito, boo)); ! }); !

Exemple !

Page 25: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

fusion n’est plus une loi de composition interne

Le type de retour Q.IPromise<Guy> n'est pas le même que celui d'entrée, Guy !

Page 26: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

function fusionPromise(guy1:Q.IPromise<Guy>, ! guy2:Q.IPromise<Guy>):Q.IPromise<Guy> { ! return Q.all([guy1, guy2]).then((guys) => fusion(guys[0], guys[1])) ! } !! var noHero:Q.IPromise<Guy> = Q(new Guy(0)); ! var heroes = [goku, vegeta, goten, trunks]; ! var promiseForHeroes:Array<Q.IPromise<Guy>> = heroes.map(Q); ! var promiseForGobejitenks = promiseForHeroes.reduce(fusionPromise, noHero); !! promiseForGobejitenks.done((gobejitenks) => { ! // gobejitenks gagne, après 1.5 secondes ! console.log(fight(gobejitenks, boo)) ! }); !

Retour de la loi de composition interne !

Page 27: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Algorithme de parcours en arbre

Si la liste contient Alors…

0 élément … on renvoie l’élément neutre

1 élément … on renvoie l’élément de la liste

2 éléments … on renvoie la fusion des deux élements

Plus de 2 éléments … on divise la liste en 2 parts et on exécute l'algorithme ici défini sur chacune des parties, avant de fusionner leurs résultats

Page 28: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

function reduceFusion(coll:Array<Q.IPromise<Guy>>):Q.IPromise<Guy> { ! var elementsCount = coll.length; ! if(elementsCount == 2) { ! return fusionPromise(coll[0], coll[1]); ! } else if(elementsCount == 1) { ! return fusionPromise[0]; ! } else if (elementsCount == 0) { ! return Q(new Guy(0)) ! } else { ! var halfSize = elementsCount / 2 ! var half1Combined = reduceFusion(coll.slice(0, halfSize)); ! var half2Combined = reduceFusion(coll.slice(halfSize, elementsCount)); ! return fusionPromise(half1Combined, half2Combined); ! } ! }; !

Attention, fonction récursive à l’horizon!

Page 29: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

var promiseForHeroes = [goku, vegeta, goten, trunks].map(Q) ! reduceFusion(promiseForHeroes).done(function(gobejitenks) { ! //gobejitenks gagne, après seulement 1s, et plus 1.5 s ! console.log(fight(gobejitenks, boo)); ! }); !

Usage !

Page 30: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Optimisation sous forme de parcours d’arbre

Page 31: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Que fallait-il pour paralléliser ?

Une loi de composition interne… •  …associative •  …avec un élément neutre

Un monoïde !

Page 32: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Abstraction de monoïde!

interface Monoid<T> { ! ZERO:T; //element neutre ! combine:(a:T, b:T) => T //loi de composition interne ! } !

Page 33: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Reduce, parallèlisation et monoïde !

var reduceMonoid = function<T>(m:Monoid<T>, coll:Array<T>):T { ! var elementsCount = coll.length; ! if(elementsCount == 2) { ! return m.combine(coll[0], coll[1]); ! } else if(elementsCount == 1) { ! return coll[0]; ! } else if (elementsCount == 0) { ! return m.ZERO! } else { ! var halfSize = elementsCount / 2 ! var firstHalf = reduceMonoid(m, coll.slice(0, halfSize)); ! var secondHalf = reduceMonoid(m, coll.slice(halfSize, elementsCount)); ! return m.combine(firstHalf, secondHalf); ! } ! }; !

Page 34: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Fusion et Reduce Monoïde !

var monoidOfPromiseOfGuy:Monoid<Q.IPromise<Guy>> = { ! ZERO: Q(new Guy(0)), ! combine: fusionPromise! }; !! var promiseForHeroes = [goku,vegeta, goten, trunks].map(Q) ! var promiseForGobejitenks = ! reduceMonoid(monoidOfPromiseOfGuy, promiseForHeroes); !

Page 35: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Quand on commence à penser monoïde!

var monoidOfBetterGuy: Monoid<Guy> = { ! ZERO:new Guy(0), ! combine:fight! }; ! var heroes = [goku,vegeta, boo, goten, trunks]; ! var winnerOfBattleRoyale = ! reduceMonoid(monoidOfBetterGuy, heroes); // renvoit boo!

On en voit partout!

Page 36: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Au fond, les monoïdes sont partout Quelques exemples :

(ensemble, opération, élément neutre)

•  (number, +, 0) •  (number, *, 1) •  (number > 0, max, 0) •  (string, '', +) •  (array, [], concat)

Page 37: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

En résumé – Le monoïde

•  Améliore la lisibilité •  Permet le parcours de liste comme un arbre •  Permet la parallélisation des traitements

Page 38: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

En résumé – Les mathématiques

Les gros mots des matheux couvrent des abstractions… •  … abstractions qui permettent la réutilisabilité d’un

concept •  … abstractions qui imposent un « contrat » aux

ensembles auxquels elles s’appliquent, comme nos interfaces

Page 39: Devoxx France 2014 - La puissance de mon Monoïde est supérieure à 9000

@benoit_lemoine #monoid9000

Des questions ?