Coding Dojo :
Programmation
Fonctionnelle

FP : pourquoi ?

Let’s say for a moment that you are a lumberjack. You have the best axe in the forest, which makes you the most productive lumberjack in the camp.

Then one day someone shows up and extols the virtues of a new tree-cutting paradigm, the chainsaw. The sales guy is persuasive, so you buy a chainsaw, but you don’t know how it works.

Demonstrating your expertise with the previous tree-cutting paradigm, you swing it vigorously at a tree—without cranking it. You quickly conclude that this newfangled chainsaw is a fad, and you return to your axe.

Then, someone appears and shows you how to crank the chainsaw...

 

“Functional Thinking: Functional Programming using Java, Clojure and Scala”,
published by O‘Reilly. 

 

FP : à quoi ca sert ?

  • Dans la vie de tous les jours, la FP sert à manipuler des collections de manière plus courte et plus expressive que des boucles for ou while
  • Il suffit principalement de 3 fonctions :
    • ForEach
    • Map
    • Filter
  • Ce coding dojo va vous expliquer ces fonctions et comment les combiner pour avoir un code plus court et plus expressif 

Les lambda fonctions 

  • Dans Programmation Fonctionnelle, il y a fonctionnelle :
    • On va combiner des fonctions au lieu de combiner des instructions 
  • Il faut donc utiliser un langage qui considère les fonctions comme des objets combinables : C#, Java 8+, Python, C++ 11, ...

  • => On va utiliser javascript car il suffit d’un navigateur pour avoir un environnement de développement opérationnel ! 

Apprenons le javascript 

  • Un tableau vide en js :
    • var people = []
       
  • Un objet en js :
    • var ben = {id:1, name: "Ben"}
    • ben.id et ben.name pour accéder aux propriétés

 

Apprenons le javascript 

  • Un tableau d’objet en js
    • var people= [{id:1,name:"Ben"}, {id:2,name:"Jafar"}, {id:3,name:"Matt"}]
       
  • Boucle en js :
    • for (var i = 0; i < people.length; i++) {
           var e = people[i];
           /*I can now use e.id and e.name*/

 

Apprenons le javascript 

  • Déclarer et appeler une fonction :
    • function test(arg1, arg2) {
          
      /*I can now use arg1 and arg2*/
          return arg1 + " " + arg2
      }
    • test("Hello", "World") => "Hello World"
       
  • Voici une fonction qu’on va utiliser ce soir
    • function display(e) { console.log(e) }; 

 

ForEach

  • on ne comprend pas facilement ce qu’on fait dans une boucle...
    • var peopleNames= ["Ben", "Jafar","Matt"]
      for (var i = 0; i < peopleNames.length; j++) {

           display(peopleNames[i])
      }

ForEach

  • => une boucle infinie qui affiche le premier nom...
     
  • forEach = appliquer une fonction sur chaque élément d’une collection
    • peopleNames.forEach(display) 

ForEach : exercice

  • afficher tous les noms à l’aide de display avec forEach
    • var people= [{id:1,name:"Ben"}, {id:2,name:"Jafar"}, {id:3,name:"Matt"}]

ForEach : exercice

  • Solution possible :
    • function printName(p) { display(p.name); } people.forEach(printName)
       
  • Petit soucis : la fonction printName fait 2 choses : elle extrait le nom et l’affiche 

 

Map

  • Quand on travaille sur un élément d’une collection, on a souvent besoin de le transformer pour qu’il soit utilisable dans une autre fonction
    • => Ca s’appelle effectuer une projection
    • function extractName(p) {
          return p.name
      }
    • => je projette le nom de la personne

Map

  • map = appliquer une projection à tous les éléments d’une collection
    • [{id:1,name:"Ben"}].map(extractName) => ["Ben"] 

Map : exercice

 

  • afficher tous les noms à l’aide de display avec forEach et map
    • var people= [{id:1,name:"Ben"}, {id:2,name:"Jafar"}, {id:3,name:"Matt"}]

Map : exercice

 

  • Solution possible :
    • function extractName(p) { return p.name} people.map(extractName).forEach(display)
       
  • Petit soucis : on ne peut pas choisir de ne pas traiter un élément 

 

Filter

  • Quand on travaille sur une collection, on a souvent besoin de travailler sur certains éléments uniquement
    • => Ca s’appelle effectuer un filtre
  • Pour effectuer un filtre, on a besoin de respecter un prédicat
    • function isMale(p) {
          return p.gender == 'MALE'
      }
    • => prédicat pour filtrer les hommes

Filter

  • Filter = appliquer un prédicat à tous les éléments d’une collection
    • [{id:1,gender:"MALE"}, {id:2,gender:"FEMALE"}]
      .filter(isMale)
      => [{id:1, gender:"MALE"}] 

 

Filter : exercice

  • afficher tous les noms qui commence par
    un B à l’aide de display avec forEach et
    map et filter
    • var people= [{id:1,name:"Ben"},
      {id:2,name:"Jafar"}, {id:3,name:"Matt"}] 

Filter : exercice

  • ​Solution possible :
    • function extractName(p) { return p.name}
      function hasNameStartingWithB(p) {
        return p.name[0] == 'B'
      }
      people.filter(hasNameStartingWithB)
          .map(extractName)
          .forEach(display)
  • Petit soucis : faire la même chose avec les noms qui commencent par d’autres lettres oblige à refaire 26 fonctions 

 

Créer des fonctions 

  • Quand on travaille avec des fonctions, on a parfois besoin de fabriquer des fonctions (en général avec moins d’arguments...​)

    • function adder(x) {
        return function(y) { return x+ y};
      }

    • => crée une fonction pour ajouter x 

  • ​On parle de fonction d'ordre supérieur currifiée

Créer des fonctions 

  • Particulièrement utile avec map et filter bien sûr

  • [1, 2, 3, 4].map(adder(2)) => [3, 4, 5, 6]

  • [1, 2, 3, 4].filter(isModulo(2)) => [2, 4]

  • people
    .filter(hasNameStartingWith('C'))
    .map(extractName)
    .forEach(display)

Créer des fonctions :exercice

  • var NASDAQ = [
    {name: "ANGI", price: 31.22, stamp: new Date(2011,11,15) },
    {name: "MSFT", price: 32.32, stamp: new Date(2011,11,15) },
    {name: "GOOG", price: 150.43, stamp: new Date(2011,11,15)},
    {name: "ANGI", price: 28.44, stamp: new Date(2011,11,16)},
    {name: "GOOG", price: 199.33, stamp: new Date(2011,11,16)}

    ];

  • Afficher le prix des actions de X (ex: "GOOG" ) en date du Z (ex: 2011-11-16) 

    • ​comparer 2 dates en JS :
      d1.getTime() == d2.getTime()

Créer des fonctions :exercice

  • Solution possible
    function isActionOf(tm) { return function(a) { return a.name == tm;} }
    function isActionAt(y, m, d) {
            return function(a) { return new Date(y, m, d).getTime() == a.stamp.getTime(); }
    }

    function extractPrice(a) { return a.price; }
  • pricesNASDAQ
        .filter(isActionOf("GOOG"))
        .filter(isActionAt(2011,11,16))
        .map(extractPrice)
        .forEach(display)
  • Petit soucis : c'est lisible mais... 

 

DSL

 

  • On aimerait pouvoir lire le code comme
    on lit de l’anglais
  • Cela est possible si on arrive à créer un
    ​Domain Specific Language
    • new Prices(nasdaq)
        .of("GOOG")
        .at(2011,11,16)
        .forEach(display) 

 

Les classes en JS

  • En JS, on peut créer des classes !
    • function MyC(arg) {
        this.prop = arg;

      }
      MyC.prototype.myfn = function (arg) {
          
      return this.prop + arg;

DSL : exercice

 

  • Ecrire une classe Prices qui permet de faire
  • new Prices(nasdaq)
        .of("GOOG")
        .at(2011,11,16)
        .forEach(display)

 

DSL : exercice

  • Solution possible
  • function Prices(list) { this.list = list; }
    Prices.prototype.of = function (tm) { 
             return new Prices(this.list.filter(isActionOf(tm)))
    }
    Prices.prototype.at = function (year, month, day) {
             return new Prices(this.list.filter(isActionAt(year, month, day)))
    }
    Prices.prototype.forEach = function(fn) { this.list.forEach(fn); }
  • Bon ce n'est toujours pas de l'anglais ...
    • ​new Prices(nasdaq).of("GOOG").at(2011,11,16).forEach(display)

DSL : en vrai !

  • Dans un "vrai" langage fonctionnel, les parenthèses, les virgules, les points et les new sont optionnels (le compilateur les déduit) 

    • new Prices(NASDAQ).of("GOOG").at(2011,11,16) 
      Est équivalent à
      Prices NASDAQ of "GOOG" at 2011 11 16

  • => c’est quasiment de l’anglais !!!

Autres utilisations ?

  • La FP n’est pas seulement idéale pour manipuler les collections, elle est également idéale pour les traitements asynchrones.
  • function every(t, fn) { setTimeout(fn, t); }
    function combine(f1, f2) { return f2(f1()); }
    //exo : combiner n fonctions
  • every(SECOND(1), combine(sensor.read, display))
    every(SECOND(1), combine(sensor.read, extractTemp, display)) 

 

Autres utilisations ?

 

  • La FP est également utile pour l’écriture de GUI
  • Lancer une recherche quand l’utilisateur a tapé au moins 3 caractères sans lancer 2 fois la même recherche
  • Rx.fromEvent(input, ‘keyup’) // créer une collection d’évènements

.map(e => e.target.value) //retenir la valeur du champ texte
.filter(q => q.length > 2) //seulement si au moins 3 caractères .distinctUntilChanged() //seulement si différent du précédent .flatMapLatest(searchAndDisplay) //et envoyer le dernier à la fonction

 

Coding Dojo Programmation Fonctionnelle

By Christophe Blin

Coding Dojo Programmation Fonctionnelle

Une introduction à la programmation fonctionnelle au travers de petits exercices de code accessibles à tous

  • 2,261