+ All Categories
Home > Software > Programmation fonctionnelle en JavaScript

Programmation fonctionnelle en JavaScript

Date post: 22-Jan-2017
Category:
Upload: loic-knuchel
View: 532 times
Download: 0 times
Share this document with a friend
88
Programmation fonctionnelle en JavaScript Loïc Knuchel
Transcript

Programmation fonctionnelle en

JavaScript

Loïc Knuchel

The Obvious

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

The Headache

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

The One

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

FP is magic !

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']

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();

});

}

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;

};

Collection API

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} ]} ]}];

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;

}

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;

}

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;});

}

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.

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.

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.

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;});

}

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;});

}

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;});

}

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;});

}

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); }

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;});

}

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);

}

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});

}

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]

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]

Traiter des données complexes (flatMap)

function getPicturesToSync(claims){

return claims

.flatMap('actions')

.filter({name: 'sendPicture'})

.flatMap('pictures')

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

}

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;

}

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;

}

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});

}

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});

}

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});

}

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});

}

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});

}

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});

}

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});

}

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});

}

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});

}

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'}

]

}

*/

Quelques autres fonctions de lodash● groupBy● partition

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

return n % 2;

});

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

Quelques autres fonctions de lodash● groupBy● partition● sortBy

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

return n;

});

// Result : [1, 2, 3]

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]

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'}]

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

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

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

Basics

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

Statelesslocal reasoning

Immutability

Bénéfices

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

● Plus compréhensible

● Plus facile à réutiliser / composer

● Plus facile à tester

● Moins de bugs

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]

]

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;

}

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

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));

}

}

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);

}

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

return total / count;

}

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

return total / count;

}

function averageForArray(arr){

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

}

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);

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]

}

];

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]

}

];

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

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]

}

];

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]

}

];

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]

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);

Functionnal wayfunction formatChart(data){

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

}

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;

}

Functionnal way

function formatChart(data){

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

}

Options

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

return user.name;

}

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

return user.name;

}

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

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

return user.name;

}

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

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

Option type

Option[A]

NoneSome[A]

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

return user.name

}

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

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

}

WTF !

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

Monads

Monads

On en a déjà vu 2 :

● List[A] / Array[A]

● Option[A]

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]

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

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

The One

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


Recommended