SlideShare a Scribd company logo
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 tableau
var 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 tableau
var 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 complexes
var 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 complexes
var 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
var 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});
}
Bilan
function 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é
Bilan
function 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;
}
Programmation fonctionnelle en JavaScript
Autre exemple
var 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 exemple
var 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});
}
Bilan
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[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
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[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});
}
Bilan
La 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});
}
Bilan
La 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});
}
Bilan
La 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});
}
Bilan
La 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 effect
Effets 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
Stateless
local 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 graph
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]
}
];
// Result :
[
[55.25, 2047], // [average temperature, population]
[5.5, 3568],
[75, 1000000]
]
Code impératif
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;
}
Code impératif
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;
}
● Pas réutilisable
● Difficile à comprendre
● bugs probables
Functionnal way : sommer les températures
var 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ératures
var 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 moyenne
function average(total, count){
return total / count;
}
Functionnal way : calculer la température moyenne
function average(total, count){
return total / count;
}
function averageForArray(arr){
return average(totalForArray(0, arr), arr.length);
}
Functionnal way : calculer la température moyenne
function 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ératures
var 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ératures
var 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]
}
];
Curryfication
function 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ératures
var 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ératures
var 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ées
var populations = pluck(data, 'population'); // [2047, 3568, 1000000]
var averageTemps = pluck(data, 'temperatures').map(averageForArray); // [55.25, 5.5, 75]
Functionnal way : combiner nos données
var 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 way
function formatChart(data){
return combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population'));
}
Functionnal way
function 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
Programmation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScript
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”
Programmation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScript

More Related Content

PDF
Javascript
PDF
Redux saga: managing your side effects. Also: generators in es6
PDF
Redux Sagas - React Alicante
PDF
Groovy collection api
PPTX
Using Redux-Saga for Handling Side Effects
PDF
Say It With Javascript
PDF
Sneaking inside Kotlin features
PDF
ECMAScript 6 Review
Javascript
Redux saga: managing your side effects. Also: generators in es6
Redux Sagas - React Alicante
Groovy collection api
Using Redux-Saga for Handling Side Effects
Say It With Javascript
Sneaking inside Kotlin features
ECMAScript 6 Review

What's hot (20)

PPTX
The redux saga begins
KEY
関数潮流(Function Tendency)
PDF
Planet-HTML5-Game-Engine Javascript Performance Enhancement
PDF
미려한 UI/UX를 위한 여정
KEY
Google Guava
PDF
Let the type system be your friend
PPTX
Kotlin collections
PDF
Google Guava - Core libraries for Java & Android
PPTX
Angular2 rxjs
PDF
6. Generics. Collections. Streams
PDF
Coding in Style
PDF
JDD 2016 - Pawel Byszewski - Kotlin, why?
PDF
Martin Fowler's Refactoring Techniques Quick Reference
PDF
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
PDF
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
PDF
Rxjs vienna
ODP
Scala introduction
PDF
Compact and safely: static DSL on Kotlin
DOCX
What are arrays in java script
PDF
The core libraries you always wanted - Google Guava
The redux saga begins
関数潮流(Function Tendency)
Planet-HTML5-Game-Engine Javascript Performance Enhancement
미려한 UI/UX를 위한 여정
Google Guava
Let the type system be your friend
Kotlin collections
Google Guava - Core libraries for Java & Android
Angular2 rxjs
6. Generics. Collections. Streams
Coding in Style
JDD 2016 - Pawel Byszewski - Kotlin, why?
Martin Fowler's Refactoring Techniques Quick Reference
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Rxjs vienna
Scala introduction
Compact and safely: static DSL on Kotlin
What are arrays in java script
The core libraries you always wanted - Google Guava
Ad

Viewers also liked (20)

PDF
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
PDF
La programmation fonctionnelle en javascript / PF
DOCX
Pasar grosir di jakarta
PPT
Lessons from Psalm 4
PPTX
Media evalution final versions
PPT
Don't Forget Who You Are
PPTX
Presentation1
PDF
BreeCS Example Report - Local Deliveries Catchment Tier Lifespan
DOCX
Ciprofloxacin 500 mg
PPTX
формування поняття відсотка 5 кл.
PPTX
6 ways to save your hearing
PDF
Deloitte tmt-predictions-2014
PDF
Ida e Volta (Cover Alfonso Rubio Rodríguez)
PDF
10 Things Calvin and Hobbes explained better than anyone else
PPTX
Automobile Rollaway: Is Your Car An Accident Waiting to Happen?
PDF
cara grosir pakaian
PDF
Neuroscience and mobile app desing
PDF
PAN Studio - Experience Design
PDF
Influencers Copy
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
La programmation fonctionnelle en javascript / PF
Pasar grosir di jakarta
Lessons from Psalm 4
Media evalution final versions
Don't Forget Who You Are
Presentation1
BreeCS Example Report - Local Deliveries Catchment Tier Lifespan
Ciprofloxacin 500 mg
формування поняття відсотка 5 кл.
6 ways to save your hearing
Deloitte tmt-predictions-2014
Ida e Volta (Cover Alfonso Rubio Rodríguez)
10 Things Calvin and Hobbes explained better than anyone else
Automobile Rollaway: Is Your Car An Accident Waiting to Happen?
cara grosir pakaian
Neuroscience and mobile app desing
PAN Studio - Experience Design
Influencers Copy
Ad

Similar to Programmation fonctionnelle en JavaScript (20)

KEY
Object-Oriented JavaScript
KEY
Object-Oriented Javascript
PDF
Javascript Combinators, the “six” edition
PDF
KEY
CoffeeScript - A Rubyist's Love Affair
PDF
Javascript Combinators (2016)
PDF
Lodash js
PDF
Intro to Advanced JavaScript
PDF
JavaScript for PHP developers
PPTX
What’s new in ECMAScript 6.0
PDF
運用Closure Compiler 打造高品質的JavaScript
PDF
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
PDF
JavaScript Libraries Overview
PPTX
Dr iterate
PPT
JavaScript Needn't Hurt!
PPTX
Less ismorewithcoffeescript webdirectionsfeb2012
PPTX
Extending javascript part one
PDF
MongoDBで作るソーシャルデータ新解析基盤
PDF
"Немного о функциональном программирование в JavaScript" Алексей Коваленко
PPTX
8558537werr.pptx
Object-Oriented JavaScript
Object-Oriented Javascript
Javascript Combinators, the “six” edition
CoffeeScript - A Rubyist's Love Affair
Javascript Combinators (2016)
Lodash js
Intro to Advanced JavaScript
JavaScript for PHP developers
What’s new in ECMAScript 6.0
運用Closure Compiler 打造高品質的JavaScript
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
JavaScript Libraries Overview
Dr iterate
JavaScript Needn't Hurt!
Less ismorewithcoffeescript webdirectionsfeb2012
Extending javascript part one
MongoDBで作るソーシャルデータ新解析基盤
"Немного о функциональном программирование в JavaScript" Алексей Коваленко
8558537werr.pptx

More from Loïc Knuchel (13)

PDF
Scala bad practices, scala.io 2019
PPTX
Mutation testing, enfin une bonne mesure de la qualité des tests ?, RivieraDe...
PDF
Ionic2, les développeurs web à l'assaut du mobile, BDX I/O le 21/10/2016
PDF
Ionic2 - the raise of web developer, Riviera DEV le 17/06/2016
PDF
FP is coming... le 19/05/2016
PDF
Ionic Framework, L'avenir du mobile sera hybride, bdx.io le 16-10-2015
PDF
Ionic, ce n'est pas que de l'UI, meetup PhoneGap le 25-05-2015
PDF
Le développement mobile hybride sort du bois, Ch'ti JUG le 15-04-2015
PDF
Devoxx 2015, Atelier Ionic - 09/04/2015
PDF
Devoxx 2015, ionic chat
PDF
Ionic HumanTalks - 11/03/2015
PDF
Ionic bbl le 19 février 2015
PDF
Des maths et des recommandations - Devoxx 2014
Scala bad practices, scala.io 2019
Mutation testing, enfin une bonne mesure de la qualité des tests ?, RivieraDe...
Ionic2, les développeurs web à l'assaut du mobile, BDX I/O le 21/10/2016
Ionic2 - the raise of web developer, Riviera DEV le 17/06/2016
FP is coming... le 19/05/2016
Ionic Framework, L'avenir du mobile sera hybride, bdx.io le 16-10-2015
Ionic, ce n'est pas que de l'UI, meetup PhoneGap le 25-05-2015
Le développement mobile hybride sort du bois, Ch'ti JUG le 15-04-2015
Devoxx 2015, Atelier Ionic - 09/04/2015
Devoxx 2015, ionic chat
Ionic HumanTalks - 11/03/2015
Ionic bbl le 19 février 2015
Des maths et des recommandations - Devoxx 2014

Recently uploaded (20)

PDF
How Creative Agencies Leverage Project Management Software.pdf
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPTX
L1 - Introduction to python Backend.pptx
PPTX
ISO 45001 Occupational Health and Safety Management System
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Nekopoi APK 2025 free lastest update
PDF
System and Network Administration Chapter 2
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
Operating system designcfffgfgggggggvggggggggg
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PPTX
ai tools demonstartion for schools and inter college
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
medical staffing services at VALiNTRY
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PPTX
CHAPTER 2 - PM Management and IT Context
How Creative Agencies Leverage Project Management Software.pdf
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
L1 - Introduction to python Backend.pptx
ISO 45001 Occupational Health and Safety Management System
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Nekopoi APK 2025 free lastest update
System and Network Administration Chapter 2
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Operating system designcfffgfgggggggvggggggggg
VVF-Customer-Presentation2025-Ver1.9.pptx
ai tools demonstartion for schools and inter college
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
medical staffing services at VALiNTRY
How to Migrate SBCGlobal Email to Yahoo Easily
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Softaken Excel to vCard Converter Software.pdf
Upgrade and Innovation Strategies for SAP ERP Customers
CHAPTER 2 - PM Management and IT Context

Programmation fonctionnelle en JavaScript

  • 2. The Obvious “La programmation fonctionnelle est une manière de programmer principalement basée sur des fonctions”
  • 3. The Headache “La programmation fonctionnelle est un style de développement qui promeut les fonctions indépendantes de l’état du programme.”
  • 4. The One “La programmation fonctionnelle permet de coder de manière plus modulaire et plus productive, avec moins de code et moins de bugs”
  • 6. Transformer un tableau var 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']
  • 7. Transformer un tableau var 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(); }); }
  • 8. 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; };
  • 10. Traiter des données complexes var 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} ]} ] }];
  • 11. Traiter des données complexes var 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; }
  • 12. Traiter des données complexes var 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; }
  • 13. 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;}); }
  • 14. 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.
  • 15. 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.
  • 16. 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.
  • 17. 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;}); }
  • 18. 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;}); }
  • 19. 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;}); }
  • 20. 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;}); }
  • 21. 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); }
  • 22. 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;}); }
  • 23. 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); }
  • 24. 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}); }
  • 25. 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]
  • 26. 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]
  • 27. Traiter des données complexes (flatMap) function getPicturesToSync(claims){ return claims .flatMap('actions') .filter({name: 'sendPicture'}) .flatMap('pictures') .filter({deleted: false, sync: false}); }
  • 28. function getPicturesToSync(claims){ return claims .flatMap('actions') .filter({name: 'sendPicture'}) .flatMap('pictures') .filter({deleted: false, sync: false}); } Bilan function 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; }
  • 29. 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é Bilan function 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; }
  • 31. Autre exemple var 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}); }
  • 32. Autre exemple var 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}); }
  • 33. Bilan 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[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}); }
  • 34. Bilan 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[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}); }
  • 35. 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}); }
  • 36. Bilan La 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}); }
  • 37. Bilan La 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}); }
  • 38. Bilan La 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}); }
  • 39. Bilan La 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}); }
  • 40. 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'} ] } */
  • 41. Quelques autres fonctions de lodash ● groupBy ● partition _.partition([1, 2, 3], function(n){ return n % 2; }); // Result : [[1, 3], [2]]
  • 42. Quelques autres fonctions de lodash ● groupBy ● partition ● sortBy _.sortBy([2, 3, 1], function(n){ return n; }); // Result : [1, 2, 3]
  • 43. 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]
  • 44. 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'}]
  • 45. 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
  • 46. 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
  • 47. Quelques autres fonctions de lodash ● groupBy ● partition ● sortBy ● take / drop ● uniq ● reduce ● sum / min / max ● ...
  • 49. No side effect Effets 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
  • 52. Bénéfices ● Moins de code (~÷3 par rapport à Java) ● Plus compréhensible ● Plus facile à réutiliser / composer ● Plus facile à tester ● Moins de bugs
  • 53. Exemple: Afficher ces données dans un graph 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] } ]; // Result : [ [55.25, 2047], // [average temperature, population] [5.5, 3568], [75, 1000000] ]
  • 54. Code impératif 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; }
  • 55. Code impératif 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; } ● Pas réutilisable ● Difficile à comprendre ● bugs probables
  • 56. Functionnal way : sommer les températures var 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)); } }
  • 57. Functionnal way : sommer les températures var 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); }
  • 58. Functionnal way : calculer la température moyenne function average(total, count){ return total / count; }
  • 59. Functionnal way : calculer la température moyenne function average(total, count){ return total / count; } function averageForArray(arr){ return average(totalForArray(0, arr), arr.length); }
  • 60. Functionnal way : calculer la température moyenne function average(total, count){ return total / count; } function averageForArray(arr){ return average(totalForArray(0, arr), arr.length); } var averageTemp = averageForArray(temperatures);
  • 61. Functionnal way : récupérer les températures var 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] } ];
  • 62. Functionnal way : récupérer les températures var 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] } ];
  • 63. Curryfication function 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
  • 64. Functionnal way : récupérer les températures var 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] } ];
  • 65. Functionnal way : récupérer les températures var 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] } ];
  • 66. Functionnal way : combiner nos données var populations = pluck(data, 'population'); // [2047, 3568, 1000000] var averageTemps = pluck(data, 'temperatures').map(averageForArray); // [55.25, 5.5, 75]
  • 67. Functionnal way : combiner nos données var 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);
  • 68. Functionnal way function formatChart(data){ return combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population')); }
  • 69. Functionnal way function 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; }
  • 70. Functionnal way function formatChart(data){ return _.zip(_.pluck(data, 'temperatures').map(t => _.sum(t) / t.length), _.pluck(data, 'population')); }
  • 72. Quel est le problème ? function getName(user){ return user.name; }
  • 73. Quel est le problème ? function getName(user){ return user.name; } getName(); // ERROR: Cannot read property 'name' of undefined
  • 74. Quel est le problème ? function getName(user){ return user.name; } getName(); // ERROR: Cannot read property 'name' of undefined getName(localStorage.getItem('user')); // ERROR ???
  • 76. Option (scala) def getName(user: User): String { return user.name } def getName(userOpt: Option[User]): String { return userOpt.map(user => user.name).getOrElse("") }
  • 81. Monads On en a déjà vu 2 : ● List[A] / Array[A] ● Option[A]
  • 82. 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]
  • 83. Monads ● List ● Option ● Future ● Try ● Either ● ...
  • 84. 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
  • 85. The One “La programmation fonctionnelle permet de coder de manière plus modulaire et plus productive, avec moins de code et moins de bugs”