63
Comprendre la programmation fonctionnelle @loicknuchel

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

Embed Size (px)

Citation preview

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

Comprendre la programmation fonctionnelle

@loicknuchel

Page 2: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 3: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

Loïc KnuchelGeek passionné

Développeur Scala

Organisateur

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

Débuter avec la programmation fonctionelle ?

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

High-order function

Pure function

Immutable

Functor

Currying

Monad

Applicative

RécursifMonoid

Page 6: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 7: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 8: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

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

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

Exemple ?

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

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

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

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 !!!

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

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 !!!

Page 13: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 14: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

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

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

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

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

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

Page 17: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 18: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

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 / StableAbstraction de haut niveau

Concis / Expressif / FlexibleFocalisé sur le domaine

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

Cas pratique

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

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

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

// expected format :[ // [coords, average temperature] [[42.097002, -79.235326], 55.25], [[38.888025, -121.016225], 5.5], [[40.462512, -99.249261], 75], ...]

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

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

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

Fonctionnel

● Immutabilité

Meilleure compréhension du codeFixe les problèmes :● Asynchrone● Concurrent● Scalabilité horizontale

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

Fonctionnel

● Immutabilité

● Stateless Raisonnement local

Couplage réduit

Testabilité

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

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● ...

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

Fonctionnel

● Immutabilité

● Stateless

● Pas d’effet de bord

Raisonnement local

Couplage réduit

Composition facilitée

Testabilité

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

Fonctionnel

● Immutabilité

● Stateless

● Pas d’effet de bord

Functional core / Imperative shell

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

Fonctionnel

● Immutabilité

● Stateless

● Pas d’effet de bord

● Décomposer en fonction réutilisables

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

Moyenne des températures par point

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

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

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

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

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

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)); // 3console.log(add2(2)); // 4

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

Fonctionnel : extraire les températuresvar 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');

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

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], ... ]

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

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

Page 36: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 37: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

Bilan

● Plus facile à :○ écrire○ lire○ maintenir

● Beaucoup moins de bugs

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

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): A1def reduce[A1 >: A](op: (A1, A1) => A1): A1def forall(p: (A) => Boolean): Boolean

def take(n: Int): List[A]def drop(n: Int): List[A]def distinct: List[A]

Page 39: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 40: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

Bugs are everywhere...

function toUpperCase(list){ const ret = []; for(let i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret;}

Safe ?

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

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 !!!

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

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

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

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

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

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 !

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

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

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

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 !!!

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

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 !

Page 48: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 49: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016
Page 50: Comprendre la programmation fonctionnelle, Blend Web Mix le 02/11/2016

Option

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

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"

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

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

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

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

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

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

Fonctors !!!

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

def toWords(sentences: List[String]): List[List[[String]] = sentences.map(_.split(" ").toList)

def toWords(sentences: List[String]): List[String] = sentences.flatMap(_.split(" ").toList)

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

def toWords(sentences: List[String]): List[List[[String]] = sentences.map(_.split(" ").toList)

def toWords(sentences: List[String]): List[String] = sentences.flatMap(_.split(" ").toList)

Applicative !

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

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

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

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 !!!

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

Level up your abstractions !

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

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

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