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 8500
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);
}
fusion
est une fonction...Guy
Guy
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
fold
/inject
/reduce
var gobejitenks = [goku, vegeta, goten, trunks].reduce(fusion)
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
Guy
fusion
new Guy(0)
fusion
différée
//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))
})
fusion
n'est plus une loi de composition interneLe type de retour Q.IPromise<Guy>
n'est pas le même que celui d'entrée, Guy
reduce
en prend un coup
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
}
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);
}
};
fusion
et reduceMonoid
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
fight
est bien une loi de composition interne,
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] )
}
}