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

What's hot (20)

PPTX
The redux saga begins
Daniel Franz
 
KEY
関数潮流(Function Tendency)
riue
 
PDF
Planet-HTML5-Game-Engine Javascript Performance Enhancement
up2soul
 
PDF
미려한 UI/UX를 위한 여정
SeungChul Kang
 
KEY
Google Guava
Alexander Korotkikh
 
PDF
Let the type system be your friend
The Software House
 
PPTX
Kotlin collections
Myeongin Woo
 
PDF
Google Guava - Core libraries for Java & Android
Jordi Gerona
 
PPTX
Angular2 rxjs
Christoffer Noring
 
PDF
6. Generics. Collections. Streams
DEVTYPE
 
PDF
Coding in Style
scalaconfjp
 
PDF
JDD 2016 - Pawel Byszewski - Kotlin, why?
PROIDEA
 
PDF
Martin Fowler's Refactoring Techniques Quick Reference
Seung-Bum Lee
 
PDF
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini
 
PDF
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
PDF
Rxjs vienna
Christoffer Noring
 
ODP
Scala introduction
Alf Kristian Støyle
 
PDF
Compact and safely: static DSL on Kotlin
Dmitry Pranchuk
 
DOCX
What are arrays in java script
Miguel Silva Loureiro
 
PDF
The core libraries you always wanted - Google Guava
Mite Mitreski
 
The redux saga begins
Daniel Franz
 
関数潮流(Function Tendency)
riue
 
Planet-HTML5-Game-Engine Javascript Performance Enhancement
up2soul
 
미려한 UI/UX를 위한 여정
SeungChul Kang
 
Google Guava
Alexander Korotkikh
 
Let the type system be your friend
The Software House
 
Kotlin collections
Myeongin Woo
 
Google Guava - Core libraries for Java & Android
Jordi Gerona
 
Angular2 rxjs
Christoffer Noring
 
6. Generics. Collections. Streams
DEVTYPE
 
Coding in Style
scalaconfjp
 
JDD 2016 - Pawel Byszewski - Kotlin, why?
PROIDEA
 
Martin Fowler's Refactoring Techniques Quick Reference
Seung-Bum Lee
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
Rxjs vienna
Christoffer Noring
 
Scala introduction
Alf Kristian Støyle
 
Compact and safely: static DSL on Kotlin
Dmitry Pranchuk
 
What are arrays in java script
Miguel Silva Loureiro
 
The core libraries you always wanted - Google Guava
Mite Mitreski
 

Viewers also liked (20)

PDF
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Loïc Knuchel
 
PDF
La programmation fonctionnelle en javascript / PF
C...L, NESPRESSO, WAFAASSURANCE, SOFRECOM ORANGE
 
DOCX
Pasar grosir di jakarta
henigusnia
 
PPT
Lessons from Psalm 4
HaynesStreet
 
PPTX
Media evalution final versions
haydnbarry
 
PPT
Don't Forget Who You Are
HaynesStreet
 
PPTX
Presentation1
cathz1024
 
PDF
BreeCS Example Report - Local Deliveries Catchment Tier Lifespan
Daniel Raines Communications / BreeCS Software
 
DOCX
Ciprofloxacin 500 mg
Herman Pasir
 
PPTX
формування поняття відсотка 5 кл.
Viktoria Mikolaenko
 
PPTX
6 ways to save your hearing
Maico Audiological Services
 
PDF
Deloitte tmt-predictions-2014
Marketing4eCommerce
 
PDF
Ida e Volta (Cover Alfonso Rubio Rodríguez)
Alfonso Rubio Rodríguez
 
PDF
10 Things Calvin and Hobbes explained better than anyone else
SlydS - Presentation Design & Training Studio
 
PPTX
Automobile Rollaway: Is Your Car An Accident Waiting to Happen?
Cost U Less Direct
 
PDF
cara grosir pakaian
www.grosirdress.com
 
PDF
Neuroscience and mobile app desing
Marketing4eCommerce
 
PDF
PAN Studio - Experience Design
PANstudio
 
PDF
Influencers Copy
Astrid Ramos
 
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Loïc Knuchel
 
La programmation fonctionnelle en javascript / PF
C...L, NESPRESSO, WAFAASSURANCE, SOFRECOM ORANGE
 
Pasar grosir di jakarta
henigusnia
 
Lessons from Psalm 4
HaynesStreet
 
Media evalution final versions
haydnbarry
 
Don't Forget Who You Are
HaynesStreet
 
Presentation1
cathz1024
 
BreeCS Example Report - Local Deliveries Catchment Tier Lifespan
Daniel Raines Communications / BreeCS Software
 
Ciprofloxacin 500 mg
Herman Pasir
 
формування поняття відсотка 5 кл.
Viktoria Mikolaenko
 
6 ways to save your hearing
Maico Audiological Services
 
Deloitte tmt-predictions-2014
Marketing4eCommerce
 
Ida e Volta (Cover Alfonso Rubio Rodríguez)
Alfonso Rubio Rodríguez
 
10 Things Calvin and Hobbes explained better than anyone else
SlydS - Presentation Design & Training Studio
 
Automobile Rollaway: Is Your Car An Accident Waiting to Happen?
Cost U Less Direct
 
cara grosir pakaian
www.grosirdress.com
 
Neuroscience and mobile app desing
Marketing4eCommerce
 
PAN Studio - Experience Design
PANstudio
 
Influencers Copy
Astrid Ramos
 
Ad

Similar to Programmation fonctionnelle en JavaScript (20)

KEY
Object-Oriented JavaScript
kvangork
 
KEY
Object-Oriented Javascript
kvangork
 
PDF
Javascript Combinators, the “six” edition
Reginald Braithwaite
 
PDF
Js hacks
Nishchit Dhanani
 
KEY
CoffeeScript - A Rubyist's Love Affair
Mark
 
PDF
Javascript Combinators (2016)
Reginald Braithwaite
 
PDF
Lodash js
LearningTech
 
PDF
Intro to Advanced JavaScript
ryanstout
 
PDF
JavaScript for PHP developers
Stoyan Stefanov
 
PPTX
What’s new in ECMAScript 6.0
Eyal Vardi
 
PDF
運用Closure Compiler 打造高品質的JavaScript
taobao.com
 
PDF
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
Dmitry Soshnikov
 
PDF
JavaScript Libraries Overview
Siarhei Barysiuk
 
PPTX
Dr iterate
Timothy Roberts
 
PPT
JavaScript Needn't Hurt!
Thomas Kjeldahl Nilsson
 
PPTX
Less ismorewithcoffeescript webdirectionsfeb2012
Jo Cranford
 
PPTX
Extending javascript part one
Vijaya Anand
 
PDF
MongoDBで作るソーシャルデータ新解析基盤
Takahiro Inoue
 
PDF
"Немного о функциональном программирование в JavaScript" Алексей Коваленко
Fwdays
 
PPTX
8558537werr.pptx
ssuser8a9aac
 
Object-Oriented JavaScript
kvangork
 
Object-Oriented Javascript
kvangork
 
Javascript Combinators, the “six” edition
Reginald Braithwaite
 
CoffeeScript - A Rubyist's Love Affair
Mark
 
Javascript Combinators (2016)
Reginald Braithwaite
 
Lodash js
LearningTech
 
Intro to Advanced JavaScript
ryanstout
 
JavaScript for PHP developers
Stoyan Stefanov
 
What’s new in ECMAScript 6.0
Eyal Vardi
 
運用Closure Compiler 打造高品質的JavaScript
taobao.com
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
Dmitry Soshnikov
 
JavaScript Libraries Overview
Siarhei Barysiuk
 
Dr iterate
Timothy Roberts
 
JavaScript Needn't Hurt!
Thomas Kjeldahl Nilsson
 
Less ismorewithcoffeescript webdirectionsfeb2012
Jo Cranford
 
Extending javascript part one
Vijaya Anand
 
MongoDBで作るソーシャルデータ新解析基盤
Takahiro Inoue
 
"Немного о функциональном программирование в JavaScript" Алексей Коваленко
Fwdays
 
8558537werr.pptx
ssuser8a9aac
 
Ad

More from Loïc Knuchel (13)

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

Recently uploaded (20)

PPTX
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pptx
Varsha Nayak
 
PPTX
The Role of a PHP Development Company in Modern Web Development
SEO Company for School in Delhi NCR
 
PDF
GetOnCRM Speeds Up Agentforce 3 Deployment for Enterprise AI Wins.pdf
GetOnCRM Solutions
 
PDF
Revenue streams of the Wazirx clone script.pdf
aaronjeffray
 
PDF
Capcut Pro Crack For PC Latest Version {Fully Unlocked} 2025
hashhshs786
 
PPTX
Java Native Memory Leaks: The Hidden Villain Behind JVM Performance Issues
Tier1 app
 
PPTX
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
PDF
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
PDF
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 
PPTX
Revolutionizing Code Modernization with AI
KrzysztofKkol1
 
PPTX
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
PPTX
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
PPTX
MiniTool Power Data Recovery Full Crack Latest 2025
muhammadgurbazkhan
 
PDF
Mobile CMMS Solutions Empowering the Frontline Workforce
CryotosCMMSSoftware
 
PPTX
Human Resources Information System (HRIS)
Amity University, Patna
 
PDF
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
PPTX
Engineering the Java Web Application (MVC)
abhishekoza1981
 
PPTX
MailsDaddy Outlook OST to PST converter.pptx
abhishekdutt366
 
PPTX
Tally software_Introduction_Presentation
AditiBansal54083
 
PDF
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pptx
Varsha Nayak
 
The Role of a PHP Development Company in Modern Web Development
SEO Company for School in Delhi NCR
 
GetOnCRM Speeds Up Agentforce 3 Deployment for Enterprise AI Wins.pdf
GetOnCRM Solutions
 
Revenue streams of the Wazirx clone script.pdf
aaronjeffray
 
Capcut Pro Crack For PC Latest Version {Fully Unlocked} 2025
hashhshs786
 
Java Native Memory Leaks: The Hidden Villain Behind JVM Performance Issues
Tier1 app
 
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
Beyond Binaries: Understanding Diversity and Allyship in a Global Workplace -...
Imma Valls Bernaus
 
Revolutionizing Code Modernization with AI
KrzysztofKkol1
 
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
MiniTool Power Data Recovery Full Crack Latest 2025
muhammadgurbazkhan
 
Mobile CMMS Solutions Empowering the Frontline Workforce
CryotosCMMSSoftware
 
Human Resources Information System (HRIS)
Amity University, Patna
 
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
Engineering the Java Web Application (MVC)
abhishekoza1981
 
MailsDaddy Outlook OST to PST converter.pptx
abhishekdutt366
 
Tally software_Introduction_Presentation
AditiBansal54083
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 

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”