Un monoïde est un magma unifère associatif
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 8500JavaScript n'a pas de surcharge de ==
var boo = new Guy(8500); var goku = new Guy(4000) var fight = function (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 de le plus fort
var boo = new Guy(8500); var goku = new Guy(4000) var vegeta = new Guy(3900); var fusion = function(guy1:Guy, guy2:Guy):Guy { return new Guy(guy1.power + guy2.power); } var bejito = fusion(goku, vegeta); console.log(bejito.power) // affiche 7900 var winner = fight(bejito, boo) console.log(winner) // affiche toujours boo, car il est toujours le plus fort
var goten = new Guy(2000) var trunks = new Guy(2500) var gotenks = fusion(goten, trunks) console.log(gotenks.power) // affiche 4500 var gobejitenks = fusion(gotenks, bejito) console.log(gobejitenks) // affiche 12400 fight(gobejitenks, boo) // gobejitenks gagne
var fusion = function(guy1:Guy, guy2:Guy):Guy { return new Guy(guy1.power + guy2.power); }
var fusion = function(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
var gobejitenks = [goku, vegeta, goten, trunks].reduce(fusion)
var goku = new Guy(4000) var mrSatan = new Guy(0) var gotan = fusion(goku, mrSatan) var saku = gusion(mrSatan, goku) console.log(gotan.equals(saku)) //affiche true console.log(gotan.equals(goku)) //affiche true console.log(saku.equals(goku)) //affiche true
Un monoide est un ensemble muni d'une loi de composition interne associative et d'un élément neutre
//La fusion executera la callback de promesse qu'après 0.5 seconde var fusion = function(guy1:Guy,guy2:Guy):Q.IPromise<Guy> { var deferred = Q.defer(); setTimeout(() => { deferred.resolve(new Guy(guy1.power + guy2.power)) }, 500); return deferred.promise; };
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((bejito:Guy) => { // affiche boo, une fois que la fusion est fini console.log(fight(bejito, boo)) })
Le type de retour Q.IPromise<Guy> n'est pas le même que celui d'entrée, Guy
var heroes = [goku, vegeta, goten, trunks]; var promiseForGobejitenks:Q.IPromise<Guy> = heroes.reduce( (acc:Q.IPromise<Guy>, guy:Guy) => { return acc.then((fusionned) => fusion(fusionned, guy)) }, Q(new Guy(0)) ) promiseForGobejitenks.done((gobejitenks) => { // gobejitenks gagne, après que les fusions aient eu lieues // Soit 1.5 secondes console.log(fight(gobejitenks, boo)) })
var fusionPromise = function(guy1:Q.IPromise<Guy>, guy2:Q.IPromise<Guy>):Q.IPromise<Guy> { //execute le then quand les 2 promesses sont résolues 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)) })
var reduceFusion = function(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); } };
var promiseForHeroes = [goku, vegeta, goten, trunks].map(Q) reduceFusion(promiseForHeroes).done((gobejitenks) => { //gobejitenks gagne, après seulement 1s, et plus 1.5 s console.log(fight(gobejitenks, boo)) })
On retrouve notre monoïde
interface Monoid<T> { ZERO:T; //element neutre combine:(a:T, b:T) => T //loi de composition interne }Le système de type ne permet pas d'indiquer l'associativité
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); } };
var monoidOfPromiseOfGuy:Monoid<Q.IPromise<Guy>> = { ZERO: Q(new Guy(0)), combine: fusionPromise }; var promiseForHeroes = [goku,vegeta, goten, trunsk].map(Q) var promiseForGobejitenks = reduceMonoid(monoidOfPromiseOfGuy, promiseForHeroes);
var monoidOfBetterGuy: Monoid<Guy> = { ZERO:new Guy(0), combine:fight } var heroes = [goku,vegeta, boo, goten, trunks] var winnerOfBattleRoyale = reduceMonoid(monoidOfBetterGuy, heroes) // renvoit boo
var fight = function (guy1:Guy, guy2:Guy):Guy { return guy1.power > guy2.power ? guy1 : guy2 } var mrSatan = new Guy(0); //vaut true fight(goku,fight(boo, vegeta)).equals(fight(fight(goku, boo), vegeta)) fight(goku, mrSatan).equal(goku) // vaut true fight(mrSatan, goku).equal(goku) // vaut true
(ensemble, opération, élément neutre)
//exemple de monoide : { ZERO: new Guy(0), combine: (a, b) => new Guy(a.power + b.power) }; { ZERO: new Guy(1), combine: (a, b) => new Guy(a.power * b.power) };
class Guy { constructor(public power:number, public name:string) {} } //exemple de monoide : { ZERO: new Guy(0, ""), combine: (a, b) => new Guy(a.power + b.power, a.name + b.name) };
{ ZERO: Some(0), combine: (a:Option<number>, b:Option<number>) => { return a.flatMap(c => b.map( d => c + d)) } }
{ ZERO: Q(0), combine: (a:Q.IPromise<number>, b:Q.IPromise<number>) => { return Q.all([a,b]).then( list => list[0] + list[1] ) } }