Programmation fonctionnelle en JavaScript

608 vues

Publié le

La programmation fonctionnelle permet de faire du code plus modulaire, avec moins de bugs et de manière plus productive !!!
Cette présentation montre comment la programmation fonctionnelle peut tenir se promesse et comment l'appliquer avec JavaScript.

Publié dans : Logiciels
0 commentaire
1 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
608
Sur SlideShare
0
Issues des intégrations
0
Intégrations
29
Actions
Partages
0
Téléchargements
10
Commentaires
0
J’aime
1
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Programmation fonctionnelle en JavaScript

  1. 1. Programmation fonctionnelle en JavaScript Loïc Knuchel
  2. 2. The Obvious “La programmation fonctionnelle est une manière de programmer principalement basée sur des fonctions”
  3. 3. The Headache “La programmation fonctionnelle est un style de développement qui promeut les fonctions indépendantes de l’état du programme.”
  4. 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”
  5. 5. FP is magic !
  6. 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. 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. 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; };
  9. 9. Collection API
  10. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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; }
  30. 30. 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}); }
  31. 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. 32. 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}); }
  33. 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. 34. 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}); }
  35. 35. 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}); }
  36. 36. 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}); }
  37. 37. 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}); }
  38. 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. 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}); }
  39. 39. 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'} ] } */
  40. 40. Quelques autres fonctions de lodash ● groupBy ● partition _.partition([1, 2, 3], function(n){ return n % 2; }); // Result : [[1, 3], [2]]
  41. 41. Quelques autres fonctions de lodash ● groupBy ● partition ● sortBy _.sortBy([2, 3, 1], function(n){ return n; }); // Result : [1, 2, 3]
  42. 42. 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]
  43. 43. 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'}]
  44. 44. 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
  45. 45. 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
  46. 46. Quelques autres fonctions de lodash ● groupBy ● partition ● sortBy ● take / drop ● uniq ● reduce ● sum / min / max ● ...
  47. 47. Basics
  48. 48. 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
  49. 49. Stateless local reasoning
  50. 50. Immutability
  51. 51. Bénéfices ● Moins de code (~÷3 par rapport à Java) ● Plus compréhensible ● Plus facile à réutiliser / composer ● Plus facile à tester ● Moins de bugs
  52. 52. 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] ]
  53. 53. 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; }
  54. 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; } ● Pas réutilisable ● Difficile à comprendre ● bugs probables
  55. 55. 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)); } }
  56. 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)); } } Vs function totalForArray(currentTotal, arr){ return arr.reduce((total, item) => total + item, currentTotal); }
  57. 57. Functionnal way : calculer la température moyenne function average(total, count){ return total / count; }
  58. 58. Functionnal way : calculer la température moyenne function average(total, count){ return total / count; } function averageForArray(arr){ return average(totalForArray(0, arr), arr.length); }
  59. 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); } var averageTemp = averageForArray(temperatures);
  60. 60. 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] } ];
  61. 61. 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] } ];
  62. 62. 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
  63. 63. 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] } ];
  64. 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')); // 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] } ];
  65. 65. 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]
  66. 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] 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);
  67. 67. Functionnal way function formatChart(data){ return combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population')); }
  68. 68. 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; }
  69. 69. Functionnal way function formatChart(data){ return _.zip(_.pluck(data, 'temperatures').map(t => _.sum(t) / t.length), _.pluck(data, 'population')); }
  70. 70. Options
  71. 71. Quel est le problème ? function getName(user){ return user.name; }
  72. 72. Quel est le problème ? function getName(user){ return user.name; } getName(); // ERROR: Cannot read property 'name' of undefined
  73. 73. Quel est le problème ? function getName(user){ return user.name; } getName(); // ERROR: Cannot read property 'name' of undefined getName(localStorage.getItem('user')); // ERROR ???
  74. 74. Option type Option[A] NoneSome[A]
  75. 75. Option (scala) def getName(user: User): String { return user.name } def getName(userOpt: Option[User]): String { return userOpt.map(user => user.name).getOrElse("") }
  76. 76. WTF ! Option.map() ??? List.map()
  77. 77. Monads
  78. 78. Monads On en a déjà vu 2 : ● List[A] / Array[A] ● Option[A]
  79. 79. 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]
  80. 80. Monads ● List ● Option ● Future ● Try ● Either ● ...
  81. 81. 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
  82. 82. The One “La programmation fonctionnelle permet de coder de manière plus modulaire et plus productive, avec moins de code et moins de bugs”

×