Top Banner
Programmation fonctionnelle en JavaScript Loïc Knuchel
88

Programmation fonctionnelle en JavaScript

Jan 22, 2017

Download

Software

Loïc Knuchel
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Programmation fonctionnelle en JavaScript

Programmation fonctionnelle en

JavaScript

Loïc Knuchel

Page 2: Programmation fonctionnelle en JavaScript

The Obvious

“La programmation fonctionnelle est une manière de programmer principalement basée sur des fonctions”

Page 3: Programmation fonctionnelle en JavaScript

The Headache

“La programmation fonctionnelle est un style de développement qui promeut les fonctions indépendantes de l’état du programme.”

Page 4: Programmation fonctionnelle en JavaScript

The One

“La programmation fonctionnelle permet de coder de manière plus modulaire et plus productive, avec moins de code et moins de bugs”

Page 5: Programmation fonctionnelle en JavaScript

FP is magic !

Page 6: Programmation fonctionnelle en JavaScript

Transformer un tableauvar names = ['Finn', 'Rey', 'Poe', 'Kaylo'];

function upperCaseArray(arr){

var ret = [];

for(var i=0; i<arr.length; i++){

ret[i] = arr[i].toUpperCase();

}

return ret;

}

console.log(upperCaseArray(names));

// ['FINN', 'REY', 'POE', 'KAYLO']

Page 7: Programmation fonctionnelle en JavaScript

Transformer un tableauvar names = ['Finn', 'Rey', 'Poe', 'Kaylo'];

function upperCaseArray(arr){

var ret = [];

for(var i=0; i<arr.length; i++){

ret[i] = arr[i].toUpperCase();

}

return ret;

}

console.log(upperCaseArray(names));

// ['FINN', 'REY', 'POE', 'KAYLO']

function upperCaseArray(arr){

return arr.map(function(item){

return item.toUpperCase();

});

}

Page 8: Programmation fonctionnelle en JavaScript

Créer son .map()

Array.prototype.map = function(callback){

var array = this;

var result = [];

for(var i=0; i<array.length; i++){

result[i] = callback(array[i]);

}

return result;

};

Page 9: Programmation fonctionnelle en JavaScript

Collection API

Page 10: Programmation fonctionnelle en JavaScript

Traiter des données complexesvar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];

Page 11: Programmation fonctionnelle en JavaScript

Traiter des données complexesvar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];

function doSomething(claims){

var pictures = [];

for(var i=0; i<claims.length; i++){

var claim = claims[i];

for(var j=0; j<claim.actions.length; j++){

var action = claim.actions[j];

if(action.name === 'sendPicture'){

for(var k=0; k<action.pictures.length; k++){

var picture = action.pictures[k];

if(!picture.deleted && !picture.sync){

pictures.push(picture);

}

}

}

}

}

return pictures;

}

Page 12: Programmation fonctionnelle en JavaScript

Traiter des données complexesvar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];

function doSomething(claims){

var pictures = [];

for(var i=0; i<claims.length; i++){

var claim = claims[i];

for(var j=0; j<claim.actions.length; j++){

var action = claim.actions[j];

if(action.name === 'sendPicture'){

for(var k=0; k<action.pictures.length; k++){

var picture = action.pictures[k];

if(!picture.deleted && !picture.sync){

pictures.push(picture);

}

}

}

}

}

return pictures;

}

Page 13: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

Page 14: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

_.map(array, callback) crée un tableau avec les valeurs retournées par le callback en paramètre.

Page 15: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

_.flatten(array) crée un tableau simple à partir d’un tableau de tableaux.

Page 16: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

_.filter(array, callback) crée un tableau en gardant que les éléments pour lesquels le callback renvoi true.

Page 17: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

Page 18: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

Page 19: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

Page 20: Programmation fonctionnelle en JavaScript

Traiter des données complexes (lodash)

function getPicturesToSync(claims){

var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));

var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function

(action){ return action.pictures; }));

return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});

}

Page 21: Programmation fonctionnelle en JavaScript

Améliorer les tableaux JavaScript

Array.prototype.find = function(callback){ return _.find(this, callback); }

Array.prototype.filter = function(callback){ return _.filter(this, callback); }

Array.prototype.map = function(callback){ return _.map(this, callback); }

Array.prototype.flatten = function() { return _.flatten(this); }

Page 22: Programmation fonctionnelle en JavaScript

Traiter des données complexes (js array)

function getPicturesToSync(claims){

return claims

.map(function(claim){ return claim.actions; })

.flatten()

.filter(function(action){ return action.name === 'sendPicture'; })

.map(function(action){ return action.pictures; })

.flatten()

.filter(function(picture){ return picture.deleted === false && picture.sync === false;});

}

Page 23: Programmation fonctionnelle en JavaScript

Traiter des données complexes (es6 fat arrow)

function getPicturesToSync(claims){

return claims

.map(claim => claim.actions)

.flatten()

.filter(action => action.name === 'sendPicture')

.map(action => action.pictures)

.flatten()

.filter(picture => picture.deleted === false && picture.sync === false);

}

Page 24: Programmation fonctionnelle en JavaScript

Traiter des données complexes (pluck)

function getPicturesToSync(claims){

return claims

.map('actions')

.flatten()

.filter({name: 'sendPicture'})

.map('pictures')

.flatten()

.filter({deleted: false, sync: false});

}

Page 25: Programmation fonctionnelle en JavaScript

Traiter des données complexes (flatMap)

var data = [

{id: '1', values: [1, 2, 3]},

{id: '2', values: [4, 5]},

];

data.map('values'); // [[1, 2, 3], [4, 5]]

data.map('values').flatten(); // [1, 2, 3, 4, 5]

Page 26: Programmation fonctionnelle en JavaScript

Traiter des données complexes (flatMap)

Array.prototype.flatMap = function(callback){ return _.flatten(_.map(this, callback)); }

var data = [

{id: '1', values: [1, 2, 3]},

{id: '2', values: [4, 5]},

];

data.map('values'); // [[1, 2, 3], [4, 5]]

data.map('values').flatten(); // [1, 2, 3, 4, 5]

data.flatMap('values'); // [1, 2, 3, 4, 5]

Page 27: Programmation fonctionnelle en JavaScript

Traiter des données complexes (flatMap)

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false, sync: false});

}

Page 28: Programmation fonctionnelle en JavaScript

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false, sync: false});

}

Bilanfunction getPicturesToSync(claims){

var pictures = [];

for(var i=0; i<claims.length; i++){

var claim = claims[i];

for(var j=0; j<claim.actions.length; j++){

var action = claim.actions[j];

if(action.name === 'sendPicture'){

for(var k=0; k<action.pictures.length; k++){

var picture = action.pictures[k];

if(!picture.deleted && !picture.sync){

pictures.push(picture);

}

}

}

}

}

return pictures;

}

Page 29: Programmation fonctionnelle en JavaScript

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false, sync: false});

}

● moins de code● moins de bugs● plus de productivité

Bilanfunction getPicturesToSync(claims){

var pictures = [];

for(var i=0; i<claims.length; i++){

var claim = claims[i];

for(var j=0; j<claim.actions.length; j++){

var action = claim.actions[j];

if(action.name === 'sendPicture'){

for(var k=0; k<action.pictures.length; k++){

var picture = action.pictures[k];

if(!picture.deleted && !picture.sync){

pictures.push(picture);

}

}

}

}

}

return pictures;

}

Page 30: Programmation fonctionnelle en JavaScript
Page 31: Programmation fonctionnelle en JavaScript

Autre exemplevar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];

function doSomething(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

Page 32: Programmation fonctionnelle en JavaScript

Autre exemplevar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];

function doSomething(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

Page 33: Programmation fonctionnelle en JavaScript

Bilanfunction getPictures(claims, wan){

for(var i=0; i<claims.length; i++){

var claim = claims[i];

if(claim.wan === wan){

var pictures = [];

for(var j=0; j<=claim.actions.length; j++){

var action = claim.actions[i];

if(action.name === 'sendPicture'){

for(var k=0; k<action.pictures.length; k++){

var picture = action.pictures[k];

if(picture.deleted){

pictures.push(picture);

}

}

}

}

return pictures;

}

}

}

function getPictures(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

Page 34: Programmation fonctionnelle en JavaScript

Bilanfunction getPictures(claims, wan){

for(var i=0; i<claims.length; i++){

var claim = claims[i];

if(claim.wan === wan){

var pictures = [];

for(var j=0; j<=claim.actions.length; j++){

var action = claim.actions[i];

if(action.name === 'sendPicture'){

for(var k=0; k<action.pictures.length; k++){

var picture = action.pictures[k];

if(picture.deleted){

pictures.push(picture);

}

}

}

}

return pictures;

}

}

}

function getPictures(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

Page 35: Programmation fonctionnelle en JavaScript

Bilan (correct)function getPictures(claims, wan){

for(var i=0; i<claims.length; i++){

var claim = claims[i];

if(claim.wan === wan){

var pictures = [];

for(var j=0; j<claim.actions.length; j++){

var action = claim.actions[j];

if(action.name === 'sendPicture'){

for(var k=0; k<action.pictures.length; k++){

var picture = action.pictures[k];

if(!picture.deleted){

pictures.push(picture);

}

}

}

}

return pictures;

}

}

}

function getPictures(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

Page 36: Programmation fonctionnelle en JavaScript

BilanLa programmation fonctionnelle permet de déclarer des intentions.

function getPictures(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false, sync: false});

}

Page 37: Programmation fonctionnelle en JavaScript

BilanLa programmation fonctionnelle permet de déclarer des intentions.

Au final, on ne sait pas vraiment ce qui est fait et quand.

function getPictures(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false, sync: false});

}

Page 38: Programmation fonctionnelle en JavaScript

BilanLa programmation fonctionnelle permet de déclarer des intentions.

Au final, on ne sait pas vraiment ce qui est fait et quand.

Ce qui permet de changer le fonctionnement du programme sans changer le code.

function getPictures(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false, sync: false});

}

Page 39: Programmation fonctionnelle en JavaScript

BilanLa programmation fonctionnelle permet de déclarer des intentions.

Au final, on ne sait pas vraiment ce qui est fait et quand.

Ce qui permet de changer le fonctionnement du programme sans changer le code.

Ex:

● synchrone => asynchrone● impératif => lazy (cf Lazy.js)

function getPictures(claims, wan){

return claims

.find({wan: wan}).actions

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false});

}

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

.filter({deleted: false, sync: false});

}

Page 40: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy _.groupBy(claims[0].actions, function(action){

return action.name;

});

/* Result :

{

'sendPicture': [

{name: 'sendPicture', pictures: [...]},

{name: 'sendPicture', pictures: [...]},

{name: 'sendPicture', pictures: [...]}

],

'changeStep': [

{name: 'changeStep', step: 'COM'}

]

}

*/

Page 41: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy● partition

_.partition([1, 2, 3], function(n){

return n % 2;

});

// Result : [[1, 3], [2]]

Page 42: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy● partition● sortBy

_.sortBy([2, 3, 1], function(n){

return n;

});

// Result : [1, 2, 3]

Page 43: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop

_.take([1, 2, 3, 4, 5], 3); // Result : [1, 2, 3]

_.drop([1, 2, 3, 4, 5], 1); // Result : [2, 3, 4, 5]

Page 44: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq

_.uniq(['foo', 'bar', 'foo', 'foo']);

// Result : ['foo', 'bar']

_.uniq([{wan: '1'}, {wan: '2'}, {wan: '1'}], 'wan');

// Result : [{wan: '1'}, {wan: '2'}]

Page 45: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq● reduce

_.reduce(claims, function(count, claim){

return count + claim.actions.length;

}, 0);

// Result: 6

Page 46: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq● reduce● sum / min / max

_.sum(['Finn', 'Rey', 'Poe', 'Kaylo'], function(name){

return name.length;

});

// Result : 15

Page 47: Programmation fonctionnelle en JavaScript

Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq● reduce● sum / min / max● ...

Page 48: Programmation fonctionnelle en JavaScript

Basics

Page 49: Programmation fonctionnelle en JavaScript

No side effectEffets de bord: lancer une exception/erreur, faire un appel (bdd, http, fichier…), récupérer la date

actuelle, modifier un paramètre, accéder à une variable “globale”, mettre un log

Page 50: Programmation fonctionnelle en JavaScript

Statelesslocal reasoning

Page 51: Programmation fonctionnelle en JavaScript

Immutability

Page 52: Programmation fonctionnelle en JavaScript

Bénéfices

● Moins de code (~÷3 par rapport à Java)

● Plus compréhensible

● Plus facile à réutiliser / composer

● Plus facile à tester

● Moins de bugs

Page 53: Programmation fonctionnelle en JavaScript

Exemple: Afficher ces données dans un graphvar data = [

{

name: "Jamestown",

population: 2047,

temperatures: [-34, 67, 101, 87]

},

{

name: "Awesome Town",

population: 3568,

temperatures: [-3, 4, 9, 12]

},

{

name: "Funky Town",

population: 1000000,

temperatures: [75, 75, 75, 75, 75]

}

];

// Result :

[

[55.25, 2047], // [average temperature, population]

[5.5, 3568],

[75, 1000000]

]

Page 54: Programmation fonctionnelle en JavaScript

Code impératiffunction formatChart(data){

var coords = [],

totalTemp = 0,

averageTemp = 0;

for(var i=0; i < data.length; i++){

totalTemp = 0;

for(var j=0; j < data[i].temperatures.length; j++){

totalTemp += data[i].temperatures[j];

}

averageTemp = totalTemp / data[i].temperatures.length;

coords.push([averageTemp, data[i].population]);

}

return coords;

}

Page 55: Programmation fonctionnelle en JavaScript

Code impératiffunction formatChart(data){

var coords = [],

totalTemp = 0,

averageTemp = 0;

for(var i=0; i < data.length; i++){

totalTemp = 0;

for(var j=0; j < data[i].temperatures.length; j++){

totalTemp += data[i].temperatures[j];

}

averageTemp = totalTemp / data[i].temperatures.length;

coords.push([averageTemp, data[i].population]);

}

return coords;

}

● Pas réutilisable

● Difficile à comprendre

● bugs probables

Page 56: Programmation fonctionnelle en JavaScript

Functionnal way : sommer les températuresvar totalTemp = totalForArray(0, temperatures);

// recursive to avoid loop

function totalForArray(currentTotal, arr){

if(arr.length === 0){

return currentTotal;

} else {

return totalForArray(currentTotal + arr[0], arr.slice(1));

}

}

Page 57: Programmation fonctionnelle en JavaScript

Functionnal way : sommer les températuresvar totalTemp = totalForArray(0, temperatures);

// recursive to avoid loop

function totalForArray(currentTotal, arr){

if(arr.length === 0){

return currentTotal;

} else {

return totalForArray(currentTotal + arr[0], arr.slice(1));

}

}

Vs

function totalForArray(currentTotal, arr){

return arr.reduce((total, item) => total + item, currentTotal);

}

Page 58: Programmation fonctionnelle en JavaScript

Functionnal way : calculer la température moyennefunction average(total, count){

return total / count;

}

Page 59: Programmation fonctionnelle en JavaScript

Functionnal way : calculer la température moyennefunction average(total, count){

return total / count;

}

function averageForArray(arr){

return average(totalForArray(0, arr), arr.length);

}

Page 60: Programmation fonctionnelle en JavaScript

Functionnal way : calculer la température moyennefunction average(total, count){

return total / count;

}

function averageForArray(arr){

return average(totalForArray(0, arr), arr.length);

}

var averageTemp = averageForArray(temperatures);

Page 61: Programmation fonctionnelle en JavaScript

Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){

return item.temperatures;

});

var data = [

{

name: "Jamestown",

population: 2047,

temperatures: [-34, 67, 101, 87]

},

{

name: "Awesome Town",

population: 3568,

temperatures: [-3, 4, 9, 12]

},

{

name: "Funky Town",

population: 1000000,

temperatures: [75, 75, 75, 75, 75]

}

];

Page 62: Programmation fonctionnelle en JavaScript

Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){

return item.temperatures;

});

// simplify & shorten map syntax

function getItem(propertyName){

return function(item){

return item[propertyName];

}

}

var allTemperatures = data.map(getItem('temperature'));

var data = [

{

name: "Jamestown",

population: 2047,

temperatures: [-34, 67, 101, 87]

},

{

name: "Awesome Town",

population: 3568,

temperatures: [-3, 4, 9, 12]

},

{

name: "Funky Town",

population: 1000000,

temperatures: [75, 75, 75, 75, 75]

}

];

Page 63: Programmation fonctionnelle en JavaScript

Curryficationfunction add1(b){

return 1+b;

}

console.log(add1(2)); // 3

function addCurry(a){

return function(b){

return a+b;

}

}

var add1 = addCurry(1);

var add2 = addCurry(2);

console.log(add1(2)); // 3

console.log(add2(2)); // 4

Page 64: Programmation fonctionnelle en JavaScript

Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){

return item.temperatures;

});

// simplify & shorten map syntax

function getItem(propertyName){

return function(item){

return item[propertyName];

}

}

var allTemperatures = data.map(getItem('temperature'));

var data = [

{

name: "Jamestown",

population: 2047,

temperatures: [-34, 67, 101, 87]

},

{

name: "Awesome Town",

population: 3568,

temperatures: [-3, 4, 9, 12]

},

{

name: "Funky Town",

population: 1000000,

temperatures: [75, 75, 75, 75, 75]

}

];

Page 65: Programmation fonctionnelle en JavaScript

Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){

return item.temperatures;

});

// simplify & shorten map syntax

function getItem(propertyName){

return function(item){

return item[propertyName];

}

}

var allTemperatures = data.map(getItem('temperature'));

// more concise again !

function pluck(arr, propertyName){

return arr.map(getItem(propertyName));

}

var allTemperatures = pluck(data, 'temperatures');

var data = [

{

name: "Jamestown",

population: 2047,

temperatures: [-34, 67, 101, 87]

},

{

name: "Awesome Town",

population: 3568,

temperatures: [-3, 4, 9, 12]

},

{

name: "Funky Town",

population: 1000000,

temperatures: [75, 75, 75, 75, 75]

}

];

Page 66: Programmation fonctionnelle en JavaScript

Functionnal way : combiner nos donnéesvar populations = pluck(data, 'population'); // [2047, 3568, 1000000]

var averageTemps = pluck(data, 'temperatures').map(averageForArray); // [55.25, 5.5, 75]

Page 67: Programmation fonctionnelle en JavaScript

Functionnal way : combiner nos donnéesvar populations = pluck(data, 'population'); // [2047, 3568, 1000000]

var averageTemps = pluck(data, 'temperatures').map(averageForArray); // [55.25, 5.5, 75]

function combineArrays(arr1, arr2, resultArr){

resultArr = resultArr || [];

if(arr1.length === 0 || arr2.length === 0){

return resultArr;

} else {

return combineArrays(arr1.slice(1), arr2.slice(1), resultArr.push([arr1[0], arr2[0]]));

}

}

var chartData = combineArrays(averageTemps, populations);

Page 68: Programmation fonctionnelle en JavaScript

Functionnal wayfunction formatChart(data){

return combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population'));

}

Page 69: Programmation fonctionnelle en JavaScript

Functionnal wayfunction formatChart(data){

return combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population'));

}

Vs

function formatChart(data){

var coords = [],

totalTemp = 0,

averageTemp = 0;

for(var i=0; i < data.length; i++){

totalTemp = 0;

for(var j=0; j < data[i].temperatures.length; j++){

totalTemp += data[i].temperatures[j];

}

averageTemp = totalTemp / data[i].temperatures.length;

coords.push([averageTemp, data[i].population]);

}

return coords;

}

Page 70: Programmation fonctionnelle en JavaScript

Functionnal way

function formatChart(data){

return _.zip(_.pluck(data, 'temperatures').map(t => _.sum(t) / t.length), _.pluck(data, 'population'));

}

Page 71: Programmation fonctionnelle en JavaScript

Options

Page 72: Programmation fonctionnelle en JavaScript

Quel est le problème ?function getName(user){

return user.name;

}

Page 73: Programmation fonctionnelle en JavaScript

Quel est le problème ?function getName(user){

return user.name;

}

getName(); // ERROR: Cannot read property 'name' of undefined

Page 74: Programmation fonctionnelle en JavaScript

Quel est le problème ?function getName(user){

return user.name;

}

getName(); // ERROR: Cannot read property 'name' of undefined

getName(localStorage.getItem('user')); // ERROR ???

Page 75: Programmation fonctionnelle en JavaScript

Option type

Option[A]

NoneSome[A]

Page 76: Programmation fonctionnelle en JavaScript

Option (scala)def getName(user: User): String {

return user.name

}

def getName(userOpt: Option[User]): String {

return userOpt.map(user => user.name).getOrElse("")

}

Page 77: Programmation fonctionnelle en JavaScript

WTF !

Option.map() ??? List.map()

Page 78: Programmation fonctionnelle en JavaScript

Monads

Page 79: Programmation fonctionnelle en JavaScript
Page 80: Programmation fonctionnelle en JavaScript
Page 81: Programmation fonctionnelle en JavaScript

Monads

On en a déjà vu 2 :

● List[A] / Array[A]

● Option[A]

Page 82: Programmation fonctionnelle en JavaScript

Monads

● Wrapper (context) M[A]

● Fonction map def map[A, B](f: A => B): M[A] => M[B]

● Fonction flatMap def flatMap[A, B](f: A => M[B]): M[A] => M[B]

Page 83: Programmation fonctionnelle en JavaScript

Monads● List● Option● Future● Try● Either● ...

Page 84: Programmation fonctionnelle en JavaScript

Conclusion● passer toutes les données nécessaires en paramètre● ne pas les modifier● ne pas utiliser null, les exceptions, les boucles● utiliser le moins possible les if● faire des fonctions très simples et les composer● utiliser des fonctions d’ordre supérieur

Page 85: Programmation fonctionnelle en JavaScript

The One

“La programmation fonctionnelle permet de coder de manière plus modulaire et plus productive, avec moins de code et moins de bugs”

Page 86: Programmation fonctionnelle en JavaScript
Page 87: Programmation fonctionnelle en JavaScript
Page 88: Programmation fonctionnelle en JavaScript