Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Comprendre la
programmation fonctionnelle
@loicknuchel
Loïc Knuchel
Geek passionné
Développeur Scala
Organisateur
Débuter avec la programmation
fonctionelle ?
High-order function
Pure function
Immutable
Functor
Currying
Monad
Applicative
Récursif
Monoid
Au fait, c’est quoi la programmation fonctionnelle ?
“La programmation fonctionnelle est un paradigme de programmation qui...
Exemple ?
for loops are everywhere
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].to...
for loops are everywhere
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].to...
for loops are everywhere
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].to...
for loops are everywhere
Array.prototype.map = function(transform){
const array = this;
const result = [];
for(let i=0; i<...
for loops are everywhere
Array.prototype.map = function(transform){
const array = this;
const result = [];
for(let i=0; i<...
for loops are everywhere
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].to...
Séparation technique vs métier
Array.prototype.map = function(transform){
var array = this;
var result = [];
for(var i=0; ...
Cas pratique
Température moyenne par localisation
const data = [
{
coords: [42.097002, -79.235326],
temperatures: [-34, 67, 101, 87]
},...
// expected format :
[
// [coords, average temperature]
[[42.097002, -79.235326], 55.25],
[[38.888025, -121.016225], 5.5],...
Code impératif
function chartFormat(data){
var results = [],
totalTemp = 0,
averageTemp = 0;
for(var i=0; i < data.length;...
Fonctionnel
● Immutabilité
Meilleure compréhension du code
Fixe les problèmes :
● Asynchrone
● Concurrent
● Scalabilité ho...
Fonctionnel
● Immutabilité
● Stateless Raisonnement local
Couplage réduit
Testabilité
Fonctionnel
● Immutabilité
● Stateless
● Pas d’effet de bord
Effet de bord :
● faire un appel (bdd, http, fichier…)
● récu...
Fonctionnel
● Immutabilité
● Stateless
● Pas d’effet de bord
Raisonnement local
Couplage réduit
Composition facilitée
Test...
Fonctionnel
● Immutabilité
● Stateless
● Pas d’effet de bord
Functional core / Imperative shell
Fonctionnel
● Immutabilité
● Stateless
● Pas d’effet de bord
● Décomposer en fonction réutilisables
Moyenne des températures par point
Fonctionnel : moyenne des températures
function sum(numArr, currentTotal){
currentTotal = currentTotal || 0;
if(numArr.len...
Fonctionnel : extraire les températures
var allTemperatures = data.map(function(item){
return item.temperatures;
});
const...
Curryfication
function add1(b){
return 1+b;
}
console.log(add1(2)); // 3
function addCurry(a){
return function(b){
return ...
Fonctionnel : extraire les températures
var allTemperatures = data.map(function(item){
return item.temperatures;
});
funct...
Fonctionnel : combiner nos données
var coordsList = data.mapAttr('coords'); // [[42.097, -79.235], ...]
var avgTemps = dat...
Fonctionnel : tout combiner
function chartFormat(data){
return data.mapAttr('coords').zip(data.mapAttr('temperatures').map...
Bilan
● Plus facile à :
○ écrire
○ lire
○ maintenir
● Beaucoup moins de bugs
Scala collection API (petite partie)
def map[B](f: (A) => B): List[B]
def filter(p: (A) => Boolean): List[A]
def partition...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Bugs are everywhere...
function toUpperCase(list){
const ret = [];
for(let i=0; i<list.length; i++){
ret[i] = list[i].toUp...
Option
Option
val myMap = Map("key" -> "value")
val v1: Option[String] = myMap.get("key") // Some("value")
val v2: Option[String]...
Option
def toUpperCase(list: List[String]) =
list.map(_.toUpperCase)
def toUpperCase(list: List[Option[String]]) =
list.ma...
List.map() vs Option.map() ?
List.map() vs Option.map() ?
Fonctors !!!
def toWords(sentences: List[String]): List[List[[String]] =
sentences.map(_.split(" ").toList)
def toWords(sentences: List...
def toWords(sentences: List[String]): List[List[[String]] =
sentences.map(_.split(" ").toList)
def toWords(sentences: List...
def toWords(sentences: List[String]): List[List[[String]] =
sentences.map(_.split(" ").toList)
def toWords(sentences: List...
def toWords(sentences: List[String]): List[List[[String]] =
sentences.map(_.split(" ").toList)
def toWords(sentences: List...
Level up your abstractions !
Take away
● Paramètre de fonction plutôt que donnée globale (même de classe)
● Créer des objets plutôt que de les modifier...
Références
Does the Language You Use Make a Difference ?
When DDD meets FP, good things happen
Philosohie fonctionnelle
Ur...
loicknuchel@gmail.com @loicknuchel http://loic.knuchel.org/
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Upcoming SlideShare
Loading in …5
×

Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

399 views

Published on

Vous commencez à en entendre parler de plus en plus mais vous avez encore du mal à voir ce que c’est et à comprendre de que ça change concrètement, ce talk est fait pour vous !!!
La programmation fonctionnelle est une manière de programmer basée sur les fonctions qui permet de faire du code vraiment modulaire, améliorer la qualité et limiter les bugs. Vous ne me croyez pas ? Venez voir cette session !

Published in: Software
  • Be the first to comment

Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

  1. 1. Comprendre la programmation fonctionnelle @loicknuchel
  2. 2. Loïc Knuchel Geek passionné Développeur Scala Organisateur
  3. 3. Débuter avec la programmation fonctionelle ?
  4. 4. High-order function Pure function Immutable Functor Currying Monad Applicative Récursif Monoid
  5. 5. Au fait, c’est quoi la programmation fonctionnelle ? “La programmation fonctionnelle est un paradigme de programmation qui considère le calcul en tant qu'évaluation de fonctions mathématiques.” Wikipedia “La programmation fonctionnelle est un style de programmation qui met l’accent sur les fonctions qui ne dépendent pas de l’état du programme.” Functionnal programming in scala “La programmation fonctionnelle permet de coder de manière plus productive, plus modulaire et avec moins de bugs.” Loïc Knuchel ;)
  6. 6. Exemple ?
  7. 7. for loops are everywhere function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } console.log(toUpperCase(['Finn', 'Rey', 'Poe'])); // ['FINN', 'REY', 'POE']
  8. 8. for loops are everywhere function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } console.log(toUpperCase(['Finn', 'Rey', 'Poe'])); // ['FINN', 'REY', 'POE'] Interdiction de modifier les paramètres !!!
  9. 9. for loops are everywhere function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } console.log(toUpperCase(['Finn', 'Rey', 'Poe'])); // ['FINN', 'REY', 'POE'] Boilerplate !!!
  10. 10. for loops are everywhere Array.prototype.map = function(transform){ const array = this; const result = []; for(let i=0; i<array.length; i++){ result[i] = transform(array[i]); } return result; };
  11. 11. for loops are everywhere Array.prototype.map = function(transform){ const array = this; const result = []; for(let i=0; i<array.length; i++){ result[i] = transform(array[i]); } return result; }; Fonction d’ordre supérieur
  12. 12. for loops are everywhere function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } function toUpperCase(list){ return list.map(item => item.toUpperCase()); }
  13. 13. Séparation technique vs métier Array.prototype.map = function(transform){ var array = this; var result = []; for(var i=0; i<array.length; i++){ result[i] = transform(array[i]); } return result; }; list.map(item => item.toUpperCase()); Générique / Réutilisable / Stable Abstraction de haut niveau Concis / Expressif / Flexible Focalisé sur le domaine
  14. 14. Cas pratique
  15. 15. Température moyenne par localisation const data = [ { coords: [42.097002, -79.235326], temperatures: [-34, 67, 101, 87] }, { coords: [38.888025, -121.016225], temperatures: [-3, 4, 9, 12] }, { coords: [40.462512, -99.249261], temperatures: [75, 75, 75, 75, 75] }, ... ];
  16. 16. // expected format : [ // [coords, average temperature] [[42.097002, -79.235326], 55.25], [[38.888025, -121.016225], 5.5], [[40.462512, -99.249261], 75], ... ]
  17. 17. Code impératif function chartFormat(data){ var results = [], 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; results.push([data[i].coords, averageTemp]); } return results; } ● Difficile à comprendre ● Pas réutilisable / générique ● Bugs probables ● Difficile à tester
  18. 18. Fonctionnel ● Immutabilité Meilleure compréhension du code Fixe les problèmes : ● Asynchrone ● Concurrent ● Scalabilité horizontale
  19. 19. Fonctionnel ● Immutabilité ● Stateless Raisonnement local Couplage réduit Testabilité
  20. 20. Fonctionnel ● Immutabilité ● Stateless ● Pas d’effet de bord Effet de bord : ● faire un appel (bdd, http, fichier…) ● récupérer la date actuelle ● accéder à une variable “globale” ● modifier un paramètre ● lancer une exception ● afficher un log ● ...
  21. 21. Fonctionnel ● Immutabilité ● Stateless ● Pas d’effet de bord Raisonnement local Couplage réduit Composition facilitée Testabilité
  22. 22. Fonctionnel ● Immutabilité ● Stateless ● Pas d’effet de bord Functional core / Imperative shell
  23. 23. Fonctionnel ● Immutabilité ● Stateless ● Pas d’effet de bord ● Décomposer en fonction réutilisables
  24. 24. Moyenne des températures par point
  25. 25. Fonctionnel : moyenne des températures function sum(numArr, currentTotal){ currentTotal = currentTotal || 0; if(numArr.length === 0){ return currentTotal; } else { return sum(numArr.slice(1), currentTotal + numArr[0]); } } function avg(numArr){ return sum(numArr) / numArr.length; } var averageTemp = avg(temperatures);
  26. 26. Fonctionnel : extraire les températures var allTemperatures = data.map(function(item){ return item.temperatures; }); const data = [ { coords: [42.097002, -79.235326], temperatures: [-34, 67, 101, 87] }, { coords: [38.888025, -121.016225], temperatures: [-3, 4, 9, 12] }, { coords: [40.462512, -99.249261], temperatures: [75, 75, 75, 75, 75] }, ... ];
  27. 27. 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
  28. 28. Fonctionnel : extraire les températures var allTemperatures = data.map(function(item){ return item.temperatures; }); function getAttr(attrName){ return function(item){ return item[attrName]; } } var allTemperatures = data.map(getAttr('temperatures')); function mapAttr(arr, attrName){ return arr.map(getAttr(attrName)); } Array.prototype.mapAttr = function(attrName){ return mapAttr(this, attrName); }; var allTemperatures = data.mapAttr('temperatures');
  29. 29. Fonctionnel : combiner nos données var coordsList = data.mapAttr('coords'); // [[42.097, -79.235], ...] var avgTemps = data.mapAttr('temperatures').map(avg); // [55.25, 5.5, 75, ...] function zip(arr1, arr2, resultArr){ resultArr = resultArr || []; if(arr1.length === 0 || arr2.length === 0){ return resultArr; } else { return zip(arr1.slice(1), arr2.slice(1), resultArr.concat([arr1[0], arr2[0]])); } } // zip([1, 2, 3], [‘a’, ‘b’, ‘c’]) => [[1, ‘a’], [2, ‘b’], [3, ‘c’]] Array.prototype.zip = function(other){ return zip(this, other, []); }; var chartData = coordsList.zip(avgTemps); // [ [[42.097002, -79.235326], 55.25] , [[38.888025, -121.016225], 5.5], ... ]
  30. 30. Fonctionnel : tout combiner function chartFormat(data){ return data.mapAttr('coords').zip(data.mapAttr('temperatures').map(avg)); } VS function chartFormat(data){ var results = [], 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; results.push([data[i].coords, averageTemp]); } return results; } def chartFormat(data: List[((Double, Double), List[Double])]) = data.map(_._1).zip(data.map(_._2).map(t => t.sum / t.length))
  31. 31. Bilan ● Plus facile à : ○ écrire ○ lire ○ maintenir ● Beaucoup moins de bugs
  32. 32. Scala collection API (petite partie) def map[B](f: (A) => B): List[B] def filter(p: (A) => Boolean): List[A] def partition(p: (A) => Boolean): (List[A], List[A]) def zip[B](that: List[B]): List[(A, B)] def sliding(size: Int): Iterator[List[A]] def find(p: (A) => Boolean): Option[A] def exists(p: (A) => Boolean): Boolean def flatten[B]: List[B] def flatMap[B](f: (A) => List[B]): List[B] def groupBy[K](f: (A) => K): Map[K, List[A]] def grouped(size: Int): Iterator[List[A]] def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 def reduce[A1 >: A](op: (A1, A1) => A1): A1 def forall(p: (A) => Boolean): Boolean def take(n: Int): List[A] def drop(n: Int): List[A] def distinct: List[A]
  33. 33. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } Safe ?
  34. 34. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } Unsafe ! Cannot read property 'xxx' of undefined !!!
  35. 35. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ if(typeof list[i] === 'string'){ ret[i] = list[i].toUpperCase(); } else { throw "not a string"; } } } else { throw "not an array"; } return ret; }
  36. 36. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ if(typeof list[i] === 'string'){ ret[i] = list[i].toUpperCase(); } else { ret[i] = list[i]; } } } return ret; }
  37. 37. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ if(typeof list[i] === 'string'){ ret[i] = list[i].toUpperCase(); } else { ret[i] = list[i]; } } } return ret; } Unreadable !
  38. 38. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ if(typeof list[i] === 'string'){ ret[i] = list[i].toUpperCase(); } else { ret[i] = list[i]; } } } return ret; }
  39. 39. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ if(typeof list[i] === 'string'){ ret[i] = list[i].toUpperCase(); } else { ret[i] = list[i]; } } } return ret; } not so “smart” Cannot read property 'xxx' of undefined !!!
  40. 40. Bugs are everywhere... function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } } return ret; } function toUpperCase(list){ var ret = []; if(Array.isArray(list)) { for(var i=0; i<list.length; i++){ if(typeof list[i] === 'string'){ ret[i] = list[i].toUpperCase(); } else { ret[i] = list[i]; } } } return ret; } Unsafe ! not so “smart” Unreadable !
  41. 41. Option
  42. 42. Option val myMap = Map("key" -> "value") val v1: Option[String] = myMap.get("key") // Some("value") val v2: Option[String] = myMap.get("miss") // None val v3: Option[String] = v1.map(_.toUpperCase) // Some("VALUE") val v4: Option[String] = v2.map(_.toUpperCase) // None val v5: String = v3.getOrElse("default") // "VALUE" val v6: String = v4.getOrElse("default") // "default"
  43. 43. Option def toUpperCase(list: List[String]) = list.map(_.toUpperCase) def toUpperCase(list: List[Option[String]]) = list.map(_.map(_.toUpperCase)) def toUpperCase(list: Option[List[Option[String]]]) = list.map(_.map(_.map(_.toUpperCase)))
  44. 44. List.map() vs Option.map() ?
  45. 45. List.map() vs Option.map() ? Fonctors !!!
  46. 46. def toWords(sentences: List[String]): List[List[[String]] = sentences.map(_.split(" ").toList) def toWords(sentences: List[String]): List[String] = sentences.flatMap(_.split(" ").toList)
  47. 47. def toWords(sentences: List[String]): List[List[[String]] = sentences.map(_.split(" ").toList) def toWords(sentences: List[String]): List[String] = sentences.flatMap(_.split(" ").toList) Applicative !
  48. 48. def toWords(sentences: List[String]): List[List[[String]] = sentences.map(_.split(" ").toList) def toWords(sentences: List[String]): List[String] = sentences.flatMap(_.split(" ").toList) Applicative ! Fonctor + Applicative = Monad
  49. 49. def toWords(sentences: List[String]): List[List[[String]] = sentences.map(_.split(" ").toList) def toWords(sentences: List[String]): List[String] = sentences.flatMap(_.split(" ").toList) Future[A] Option[A] List[A] Try[A] Page[A] Monads !!!
  50. 50. Level up your abstractions !
  51. 51. Take away ● Paramètre de fonction plutôt que donnée globale (même de classe) ● Créer des objets plutôt que de les modifier (immutable) ● Option plutôt que ‘null’ ● Option / Try / Either / Validation plutôt qu’une exception ● Collection API / récursivité plutôt que boucles for/while ● Eviter les ‘if’ autant que possible ● Séparation technique / métier ● Functionnal core / Imperative shell
  52. 52. Références Does the Language You Use Make a Difference ? When DDD meets FP, good things happen Philosohie fonctionnelle Ur Domain Haz Monoids (vidéo) DDD: et si on reprenait l'histoire par le bon bout ? DDD, en vrai pour le développeur Functional programming Illustrated by Scala Scala School!
  53. 53. loicknuchel@gmail.com @loicknuchel http://loic.knuchel.org/

×