FP is coming...
@loicknuchel
Loïc Knuchel
Freelance
Développeur web full-stack
Entrepreneur
Cookers / SalooN
loicknuchel@gmail.com
@loicknuchel
http://...
FP
Front-end
Fonctions pures
Immutabilité
Fonctions pures
Immutabilité
Modifier une variable ?
Accès BDD ?
Logs ? Exceptions ?
CRUD ?
Do not fear FP
Au fait, c’est quoi la programmation fonctionnelle ?
“La programmation fonctionnelle est un paradigme de programmation qui...
Au fait, c’est quoi la programmation fonctionnelle ?
“La programmation fonctionnelle est un paradigme de programmation qui...
Au fait, c’est quoi la programmation fonctionnelle ?
“La programmation fonctionnelle est un paradigme de programmation qui...
Transformer un tableau
function toUpperCase(list){
var ret = [];
for(var i=0; i<list.length; i++){
ret[i] = list[i].toUppe...
Transformer un tableau
function toUpperCase(list){
return list.map(function(item){
return item.toUpperCase();
});
}
var na...
Transformer un tableau
function toUpperCase(list){
return list.map(function(item){
return item.toUpperCase();
});
}
var na...
Créer son .map()
Array.prototype.map = function(callback){
var array = this;
var result = [];
for(var i=0; i<array.length;...
Séparation technique / métier
Array.prototype.map = function(callback){
var array = this;
var result = [];
for(var i=0; i<...
Séparation technique / métier
Array.prototype.map = function(callback){
var array = this;
var result = [];
for(var i=0; i<...
Séparation technique / métier
Array.prototype.map = function(callback){
var array = this;
var result = [];
for(var i=0; i<...
Architecture Hexagonale
Manipuler des données
var data = [{
id: '123',
actions: [
{name: 'sendPicture', pictures: [
{path: '123/1.jpg', deleted: t...
function getPictures(items, id){
var pictures = [];
for (var i=0; i<items.length; i++) {
var item = items[i];
if (item.id ...
function getPictures(items, id){
var pictures = [];
for (var i=0; i<items.length; i++) {
var item = items[i];
if (item.id ...
function getPictures(items, id){
var pictures = [];
for (var i=0; i<items.length; i++) {
var item = items[i];
if (item.id ...
function getPictures(items, id){
var pictures = [];
for (var i=0; i<items.length; i++) {
var item = items[i];
if (item.id ...
Manipuler des données
// Finds the 1st elt of the sequence satisfying a predicate, if any
def find[A](p: (A) => Boolean): ...
Manipuler des données
function getPictures(items, id){
return items
.find(function(item){ return item.id === id; }).action...
public List<Picture> getPictures(List<Item> items, String id) {
List<Picture> pictures = new ArrayList<>();
for (Item item...
public List<Picture> getPictures(List<Item> items, String id) {
List<Picture> pictures = new ArrayList<>();
for (Item item...
public List<Picture> getPictures(List<Item> items, String id) {
List<Picture> pictures = new ArrayList<>();
for (Item item...
public List<Picture> getPictures(List<Item> items, String id) {
List<Picture> pictures = new ArrayList<>();
for (Item item...
public List<Picture> getPictures(List<Item> items, String id) {
List<Picture> pictures = new ArrayList<>();
for (Item item...
Java “Safe”
public List<Picture> getPictures(List<Item> items, String id) {
List<Picture> pictures = new ArrayList<>();
if...
Option
Le problème
function getName(user) {
return user.name;
}
Le problème
function getName(user) {
return user.name;
}
public String getName(User user) {
return user.getName();
}
Le problème
function getName(user) {
return user.name;
}
public String getName(User user) {
return user.getName();
}
def g...
Le problème
function getName(user) {
return user.name;
}
getName();
// Cannot read property 'name' of
undefined
public Str...
Le problème
function getName(user) {
return user.name;
}
getName();
// Cannot read property 'name' of
undefined
getName(lo...
Le problème
function getName(user) {
return user.name;
}
getName();
// Cannot read property 'name' of
undefined
getName(lo...
Option
Le problème
function getName(user) {
return user.name;
}
getName();
// Cannot read property 'name' of
undefined
getName(lo...
List.map() vs Option.map()
Monad
Monad
● Wrapper (context) M[A]
● Fonction map def map[B](f: A => B): M[B]
● Fonction flatMap def flatMap[B](f: A => M[B]):...
Monad
● List
● Option
● Future
● Try
● ...
Basics
Typage fort
Typage fort
● Filet de sécurité pour garantir la cohérence du programme
Typage fort
● Filet de sécurité pour garantir la cohérence du programme
● Documentation pour le développeur
Typage fort
● Filet de sécurité pour garantir la cohérence du programme
● Documentation pour le développeur
● Implémentent...
Typage fort
● Filet de sécurité pour garantir la cohérence du programme
● Documentation pour le développeur
● Implémentent...
Typage fort
● Filet de sécurité pour garantir la cohérence du programme
● Documentation pour le développeur
● Implémentent...
Typage fort
● Filet de sécurité pour garantir la cohérence du programme
● Documentation pour le développeur
● Implémentent...
Typage fort
● Filet de sécurité pour garantir la cohérence du programme
● Documentation pour le développeur
● Implémentent...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Type all the things !!!
case class Contact(
firstName: String,
middleInitial: String,
lastName: String,
emailAddress: Stri...
Stateless
Stateless
Passer toute les données nécessaires à chaque fois
Stateless
Passer toute les données nécessaires à chaque fois
● Testabilité
Stateless
Passer toute les données nécessaires à chaque fois
● Testabilité
● Plus facile à comprendre
Immutabilité
Immutabilité
Immutabilité
● Scalabilité / Multithreading
Immutabilité
● Scalabilité / Multithreading
● Meilleur nommage
Immutabilité
● Scalabilité / Multithreading
● Meilleur nommage
● Séparation données / calculs
Types et Fonctions plutôt qu...
Immutabilité
● Scalabilité / Multithreading
● Meilleur nommage
● Séparation données / calculs
Types et Fonctions plutôt qu...
No side effect
Effet de bord: lancer une exception, faire un appel (bdd, http, fichier…), récupérer la date actuelle,
modi...
No side effect
No side effect
● Fonctions plus faciles à comprendre et à composer
● Possibilité de construire des choses complexes à part...
● Lancer une exception ?
No side effect
● Lancer une exception ?
No side effect
Renvoyer un Type d’erreur :
● Option[A] : un type ou pas
● Try[A] : un type ou un ...
● Lancer une exception ?
● Accès à une base de données ?
No side effect
● Lancer une exception ?
● Accès à une base de données ?
No side effect
Effet de bord fait :
● en “bordure du système”
● i...
● Lancer une exception ?
● Accès à une base de données ?
● Afficher un log ?
No side effect
● Lancer une exception ?
● Accès à une base de données ?
● Afficher un log ?
No side effect
On peut éventuellement se perm...
Architecture Hexagonale
strict FP
soft FP
“Easy to learn/write”
vs
“Easy to maintain”
DDD
Hexagonal architecture
Event Storming
Property based testing
Event Sourcing
Clean code
TDD
BDD
Craftsmanship
Living Do...
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
Ur Domain Haz Monoids (vidé...
loicknuchel@gmail.com @loicknuchel http://loic.knuchel.org/
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
FP is coming... le 19/05/2016
Prochain SlideShare
Chargement dans…5
×

FP is coming... le 19/05/2016

177 vues

Publié le

La programmation fonctionnelle est un style de programmation qui commence à se populariser. Cependant, elle garde un côté compliqué et inaccessible ce qui n'est absolument pas le cas.
Le but de cette présentation est de montrer pourquoi la programmation fonctionnelle est intéressante et surtout comment s'y mettre par petites étapes :)
Les exemples sont montrés en JavaScript / Java / Scala pour rester le plus accessible et voir les différences entre ces langages.

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

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

Aucune remarque pour cette diapositive

FP is coming... le 19/05/2016

  1. 1. FP is coming... @loicknuchel
  2. 2. Loïc Knuchel Freelance Développeur web full-stack Entrepreneur Cookers / SalooN loicknuchel@gmail.com @loicknuchel http://loic.knuchel.org/ Geek passionné
  3. 3. FP
  4. 4. Front-end
  5. 5. Fonctions pures Immutabilité
  6. 6. Fonctions pures Immutabilité Modifier une variable ? Accès BDD ? Logs ? Exceptions ? CRUD ?
  7. 7. Do not fear FP
  8. 8. 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
  9. 9. 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
  10. 10. 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 et plus modulaire, avec moins de bugs.” Moi
  11. 11. Transformer un tableau function toUpperCase(list){ var ret = []; for(var i=0; i<list.length; i++){ ret[i] = list[i].toUpperCase(); } return ret; } var names = ['Finn', 'Rey', 'Poe']; console.log(toUpperCase(names)); // ['FINN', 'REY', 'POE'] public List<String> toUpperCase(List<String> list) { List<String> ret = new ArrayList<>(); for(String item : list){ ret.add(item.toUpperCase()); } return ret; } List<String> names = Arrays.asList("Finn", "Rey", "Poe"); System.out.println(Arrays.toString(toUpperCase(names). toArray())); // [FINN, REY, POE]
  12. 12. Transformer un tableau function toUpperCase(list){ return list.map(function(item){ return item.toUpperCase(); }); } var names = ['Finn', 'Rey', 'Poe']; console.log(toUpperCase(names)); // ['FINN', 'REY', 'POE'] def toUpperCase(list: List[String]): List[String] = list.map(item => item.toUpperCase) val names = List("Finn", "Rey", "Poe") println(toUpperCase(names)) // List(FINN, REY, POE)
  13. 13. Transformer un tableau function toUpperCase(list){ return list.map(function(item){ return item.toUpperCase(); }); } var names = ['Finn', 'Rey', 'Poe']; console.log(toUpperCase(names)); // ['FINN', 'REY', 'POE'] def toUpperCase(list: List[String]): List[String] = list.map(_.toUpperCase) val names = List("Finn", "Rey", "Poe") println(toUpperCase(names)) // List(FINN, REY, POE)
  14. 14. 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; };
  15. 15. Séparation technique / métier 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; }; list.map(function(item){ return item.toUpperCase(); });
  16. 16. Séparation technique / métier 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; }; list.map(function(item){ return item.toUpperCase(); }); Générique Haut niveau d’abstraction Un maximum de libraires externe
  17. 17. Séparation technique / métier 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; }; list.map(function(item){ return item.toUpperCase(); }); Générique Haut niveau d’abstraction Un maximum de libraires externe Concis / Expressif Focalisé sur le domaine Un minimum de libraires externe
  18. 18. Architecture Hexagonale
  19. 19. Manipuler des données var data = [{ id: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {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} ]} ] }, { id: '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(items, id){ var pictures = []; for (var i=0; i<items.length; i++) { var item = items[i]; if (item.id === id) { for (var j=0; j<item.actions.length; j++) { var action = item.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; }
  20. 20. function getPictures(items, id){ var pictures = []; for (var i=0; i<items.length; i++) { var item = items[i]; if (item.id === id) { for (var j=0; j<item.actions.length; j++) { var action = item.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; } Manipuler des données public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; }
  21. 21. function getPictures(items, id){ var pictures = []; for (var i=0; i<items.length; i++) { var item = items[i]; if (item.id === id) { for (var j=0; j<item.actions.length; j++) { var action = item.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; } Manipuler des données public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } Duplication ! Duplication !
  22. 22. function getPictures(items, id){ var pictures = []; for (var i=0; i<items.length; i++) { var item = items[i]; if (item.id === id) { for (var j=0; j<item.actions.length; j++) { var action = item.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; } Manipuler des données public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } Find item by id Filter actions by name Filter pictures not deleted
  23. 23. function getPictures(items, id){ var pictures = []; for (var i=0; i<items.length; i++) { var item = items[i]; if (item.id === id) { for (var j=0; j<item.actions.length; j++) { var action = item.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; } Manipuler des données public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } Find item by id Filter actions by name Filter pictures not deleted Level up your abstraction !
  24. 24. Manipuler des données // Finds the 1st elt of the sequence satisfying a predicate, if any def find[A](p: (A) => Boolean): Option[A] // Selects all elts of this collection which satisfy a predicate def filter[A](p: (A) => Boolean): List[A] // Builds a new list by applying a function to all elements of the list def map[A, B](f: (A) => B): List[B] // Applies a binary operator to all elts of this list def reduce[A, B](f: (B, A) => B, b: B): B
  25. 25. Manipuler des données function getPictures(items, id){ return items .find(function(item){ return item.id === id; }).actions .filter(function(action){ return action.name === 'sendPicture'; }) .map(function(action){ return action.pictures; }) .reduce(function(a, b){ return a.concat(b); }, []) .filter(function(picture){ return !picture.deleted; }); } def getPictures(items: List[Item], id: String): List[Picture] = items .find(_.id == id) .map(_.actions).getOrElse(List()) .filter(_.name == "sendPicture") .flatMap(_.pictures) .filter(!_.deleted)
  26. 26. public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } def getPictures(items: List[Item], id: String): List[Picture] = items .find(_.id == id) .map(_.actions).getOrElse(List()) .filter(_.name == "sendPicture") .flatMap(_.pictures) .filter(!_.deleted) Manipuler des données function getPictures(items, id){ var pictures = []; for(var i=0; i<items.length; i++){ var item = items[i]; if(item.id === id){ for(var j=0; j<item.actions.length; j++){ var action = item.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(items, id){ return items .find(function(item){ return item.id === id; }).actions .filter(function(action){ return action.name === 'sendPicture'; }) .map(function(action){ return action.pictures; }) .reduce(function(a, b){ return a.concat(b); }, []) .filter(function(picture){ return !picture.deleted; }); } Safe ?
  27. 27. public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } def getPictures(items: List[Item], id: String): List[Picture] = items .find(_.id == id) .map(_.actions).getOrElse(List()) .filter(_.name == "sendPicture") .flatMap(_.pictures) .filter(!_.deleted) Manipuler des données function getPictures(items, id){ var pictures = []; for(var i=0; i<items.length; i++){ var item = items[i]; if(item.id === id){ for(var j=0; j<item.actions.length; j++){ var action = item.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(items, id){ return items .find(function(item){ return item.id === id; }).actions .filter(function(action){ return action.name === 'sendPicture'; }) .map(function(action){ return action.pictures; }) .reduce(function(a, b){ return a.concat(b); }, []) .filter(function(picture){ return !picture.deleted; }); } Cannot read property 'xxx' of undefined x 6 !!!
  28. 28. public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } def getPictures(items: List[Item], id: String): List[Picture] = items .find(_.id == id) .map(_.actions).getOrElse(List()) .filter(_.name == "sendPicture") .flatMap(_.pictures) .filter(!_.deleted) Manipuler des données function getPictures(items, id){ var pictures = []; for(var i=0; i<items.length; i++){ var item = items[i]; if(item.id === id){ for(var j=0; j<item.actions.length; j++){ var action = item.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(items, id){ return items .find(function(item){ return item.id === id; }).actions .filter(function(action){ return action.name === 'sendPicture'; }) .map(function(action){ return action.pictures; }) .reduce(function(a, b){ return a.concat(b); }, []) .filter(function(picture){ return !picture.deleted; }); } Cannot read property 'xxx' of undefined x 6 !!!
  29. 29. public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } def getPictures(items: List[Item], id: String): List[Picture] = items .find(_.id == id) .map(_.actions).getOrElse(List()) .filter(_.name == "sendPicture") .flatMap(_.pictures) .filter(!_.deleted) Manipuler des données function getPictures(items, id){ var pictures = []; for(var i=0; i<items.length; i++){ var item = items[i]; if(item.id === id){ for(var j=0; j<item.actions.length; j++){ var action = item.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(items, id){ return items .find(function(item){ return item.id === id; }).actions .filter(function(action){ return action.name === 'sendPicture'; }) .map(function(action){ return action.pictures; }) .reduce(function(a, b){ return a.concat(b); }, []) .filter(function(picture){ return !picture.deleted; }); } Cannot read property 'xxx' of undefined x 6 !!! java.lang.NullPointerException x 6 !!!
  30. 30. public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); for (Item item : items) { if (item.getId() == id) { for (Action action : item.getActions()) { if (action.getName() == "sendPicture") { for (Picture picture : action.getPictures()) { if (!picture.getDeleted()) { pictures.add(picture); } } } } } } return pictures; } def getPictures(items: List[Item], id: String): List[Picture] = items .find(_.id == id) .map(_.actions).getOrElse(List()) .filter(_.name == "sendPicture") .flatMap(_.pictures) .filter(!_.deleted) Manipuler des données function getPictures(items, id){ var pictures = []; for(var i=0; i<items.length; i++){ var item = items[i]; if(item.id === id){ for(var j=0; j<item.actions.length; j++){ var action = item.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(items, id){ return items .find(function(item){ return item.id === id; }).actions .filter(function(action){ return action.name === 'sendPicture'; }) .map(function(action){ return action.pictures; }) .reduce(function(a, b){ return a.concat(b); }, []) .filter(function(picture){ return !picture.deleted; }); } Cannot read property 'xxx' of undefined x 6 !!! java.lang.NullPointerException x 6 !!! Safe code o/
  31. 31. Java “Safe” public List<Picture> getPictures(List<Item> items, String id) { List<Picture> pictures = new ArrayList<>(); if (items != null) { for (Item item : items) { if (item != null && item.getId() == id && item.getActions() != null) { for (Action action : item.getActions()) { if (action != null && action.getName() == "sendPicture" && action.getPictures() != null) { for (Picture picture : action.getPictures()) { if (picture != null && !picture.getDeleted()) { pictures.add(picture); } } } } } } } return pictures; } def getPictures(items: List[Item], id: String): List[Picture] = items .find(_.id == id) .map(_.actions).getOrElse(List()) .filter(_.name == "sendPicture") .flatMap(_.pictures) .filter(!_.deleted)
  32. 32. Option
  33. 33. Le problème function getName(user) { return user.name; }
  34. 34. Le problème function getName(user) { return user.name; } public String getName(User user) { return user.getName(); }
  35. 35. Le problème function getName(user) { return user.name; } public String getName(User user) { return user.getName(); } def getUser(user: User): String = user.name
  36. 36. Le problème function getName(user) { return user.name; } getName(); // Cannot read property 'name' of undefined public String getName(User user) { return user.getName(); } getName(null); // java.lang.NullPointerException def getUser(user: User): String = user.name // no null (used) in scala !
  37. 37. Le problème function getName(user) { return user.name; } getName(); // Cannot read property 'name' of undefined getName(localStorage.getItem('user')); // ERROR ??? public String getName(User user) { return user.getName(); } getName(null); // java.lang.NullPointerException getName(getUser()); // ERROR ??? def getUser(user: User): String = user.name // no null (used) in scala !
  38. 38. Le problème function getName(user) { return user.name; } getName(); // Cannot read property 'name' of undefined getName(localStorage.getItem('user')); // ERROR ??? function getName(user) { return user ? user.name : ''; } function getName(user) { return (user || {}).name; } public String getName(User user) { return user.getName(); } getName(null); // java.lang.NullPointerException getName(getUser()); // ERROR ??? public String getName(User user) { if(user != null){ return user.getName(); } else { return ""; } } def getUser(user: User): String = user.name // no null (used) in scala !
  39. 39. Option
  40. 40. Le problème function getName(user) { return user.name; } getName(); // Cannot read property 'name' of undefined getName(localStorage.getItem('user')); // ERROR ??? function getName(user) { return user ? user.name : ''; } function getName(user) { return (user || {}).name; } public String getName(User user) { return user.getName(); } getName(null); // java.lang.NullPointerException getName(getUser()); // ERROR ??? public String getName(User user) { if(user != null){ return user.getName(); } else { return ""; } } def getUser(user: User): String = user.name // no null (used) in scala ! def getUser(user: Option[User]): Option [String] = user.map(_.name) def getUser(user: Option[User]): String = user.map(_.name).getOrElse("")
  41. 41. List.map() vs Option.map()
  42. 42. Monad
  43. 43. Monad ● Wrapper (context) M[A] ● Fonction map def map[B](f: A => B): M[B] ● Fonction flatMap def flatMap[B](f: A => M[B]): M[B]
  44. 44. Monad ● List ● Option ● Future ● Try ● ...
  45. 45. Basics
  46. 46. Typage fort
  47. 47. Typage fort ● Filet de sécurité pour garantir la cohérence du programme
  48. 48. Typage fort ● Filet de sécurité pour garantir la cohérence du programme ● Documentation pour le développeur
  49. 49. Typage fort ● Filet de sécurité pour garantir la cohérence du programme ● Documentation pour le développeur ● Implémentent certains concepts
  50. 50. Typage fort ● Filet de sécurité pour garantir la cohérence du programme ● Documentation pour le développeur ● Implémentent certains concepts ● null => Option
  51. 51. Typage fort ● Filet de sécurité pour garantir la cohérence du programme ● Documentation pour le développeur ● Implémentent certains concepts ● null => Option ● exception => Either / Try
  52. 52. Typage fort ● Filet de sécurité pour garantir la cohérence du programme ● Documentation pour le développeur ● Implémentent certains concepts ● null => Option ● exception => Either / Try ● async => Future ● ...
  53. 53. Typage fort ● Filet de sécurité pour garantir la cohérence du programme ● Documentation pour le développeur ● Implémentent certains concepts ● Type Driven Development
  54. 54. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean )
  55. 55. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean ) Optionnel ?
  56. 56. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean ) Optionnel ? Contrainte ?
  57. 57. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean ) Optionnel ? Contrainte ? Lien ?
  58. 58. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean ) Optionnel ? Contrainte ? Lien ? Logique métier ?
  59. 59. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean ) case class Contact( name: PersonalName, email: EmailAddress) Optionnel ? Contrainte ? Lien ? Logique métier ?
  60. 60. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean ) case class Contact( name: PersonalName, email: EmailAddress) case class PersonalName( firstName: String_50, middleInitial: Option[String_1], lastName: String_50) sealed trait EmailAddress case class VerifiedEmail(value: Email) extends EmailAddress case class UnverifiedEmail(value: Email) extends EmailAddress Optionnel ? Contrainte ? Lien ? Logique métier ?
  61. 61. Type all the things !!! case class Contact( firstName: String, middleInitial: String, lastName: String, emailAddress: String, isEmailVerified: Boolean ) case class Contact( name: PersonalName, email: EmailAddress) case class PersonalName( firstName: String_50, middleInitial: Option[String_1], lastName: String_50) sealed trait EmailAddress case class VerifiedEmail(value: Email) extends EmailAddress case class UnverifiedEmail(value: Email) extends EmailAddress case class String_1(value: String) { require(value.length <= 1, s"String_1 should be <= 1 (actual: $value)") override def toString: String = value } case class String_50(value: String) { require(value.length <= 50, s"String_50 should be <= 50 (actual: $value)") override def toString: String = value } case class Email(value: String) { require(value.contains("@"), s"Email should contain '@' (actual: $value)") override def toString: String = value } Optionnel ? Contrainte ? Lien ? Logique métier ?
  62. 62. Stateless
  63. 63. Stateless Passer toute les données nécessaires à chaque fois
  64. 64. Stateless Passer toute les données nécessaires à chaque fois ● Testabilité
  65. 65. Stateless Passer toute les données nécessaires à chaque fois ● Testabilité ● Plus facile à comprendre
  66. 66. Immutabilité
  67. 67. Immutabilité
  68. 68. Immutabilité ● Scalabilité / Multithreading
  69. 69. Immutabilité ● Scalabilité / Multithreading ● Meilleur nommage
  70. 70. Immutabilité ● Scalabilité / Multithreading ● Meilleur nommage ● Séparation données / calculs Types et Fonctions plutôt que Classes : ● Entity ● Value Object ● Service
  71. 71. Immutabilité ● Scalabilité / Multithreading ● Meilleur nommage ● Séparation données / calculs Types et Fonctions plutôt que Classes : ● Entity ● Value Object ● Service case class Person( firstName: String, lastName: String) { val fullName = Person.fullName(this) } object Person { def fullName(p: Person): String = p.firstName+" "+p.lastName }
  72. 72. No side effect Effet de bord: lancer une exception, faire un appel (bdd, http, fichier…), récupérer la date actuelle, modifier un paramètre, accéder à une variable “globale”, afficher un log...
  73. 73. No side effect
  74. 74. No side effect ● Fonctions plus faciles à comprendre et à composer ● Possibilité de construire des choses complexes à partir d’éléments simples ● Local reasoning
  75. 75. ● Lancer une exception ? No side effect
  76. 76. ● Lancer une exception ? No side effect Renvoyer un Type d’erreur : ● Option[A] : un type ou pas ● Try[A] : un type ou un Throwable ● Either[A, B] : un type ou un autre ● Validation[A, Seq[ValidationError]] : un type ou une liste d’erreur
  77. 77. ● Lancer une exception ? ● Accès à une base de données ? No side effect
  78. 78. ● Lancer une exception ? ● Accès à une base de données ? No side effect Effet de bord fait : ● en “bordure du système” ● idéalement par une librairie ● représenté par un type (Future, IO…) Ex : def getDBUser(id: UserId): Future[Option[User]] = ???
  79. 79. ● Lancer une exception ? ● Accès à une base de données ? ● Afficher un log ? No side effect
  80. 80. ● Lancer une exception ? ● Accès à une base de données ? ● Afficher un log ? No side effect On peut éventuellement se permettre un peu de liberté...
  81. 81. Architecture Hexagonale strict FP soft FP
  82. 82. “Easy to learn/write” vs “Easy to maintain”
  83. 83. DDD Hexagonal architecture Event Storming Property based testing Event Sourcing Clean code TDD BDD Craftsmanship Living Documentation CQRS
  84. 84. 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’ ● Either/Try plutôt qu’une exception ● Collection API / recursivité plutôt que boucles for/while ● Eviter les ‘if’ autant que possible ● Séparation métier / technique
  85. 85. Références Does the Language You Use Make a Difference ? When DDD meets FP, good things happen 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!
  86. 86. loicknuchel@gmail.com @loicknuchel http://loic.knuchel.org/

×