0 Introduction Langages fonctionnels Programmation fonctionnelle en JS - Part I Programmation fonctionnelle en JS - Part II Remaques sur la performance Conclusion Quizz

Programmation fonctionnelle en JavaScript :
🦄 ou 💩?

Speaker

Knowledge

Langages pratiqués

  1. Java
  2. JavaScript (TypeScript, CoffeeScript)
  3. Kotlin, Scala

Notions dans

  • Go
  • Python
  • Racket
  • Rust
  • SML
  • Swift
  • ...

🗺 Plan

Langages fonctionnels

WarmUp

Fooling around with alternating current (AC) is just a waste of time. Nobody will use it, ever.

Edison, 1889 inventeur, scientifique, fondateur de General Electric

There is no reason anyone would want a computer in their home.

Ken Olson, 1977 cofondateur DEC

I predict the Internet will soon go spectacularly supernova and in 1996 catastrophically collapse.

Robert Metcalfe, 1995 inventeur ethernet, fondateur de 3com

Catharsis

C est un langage fonctionnel

JavaScript est un langage fonctionnel

JQuery est une monade

Paradigmes

Paradigmes

  • programmation impérative
  • programmation orientée objet
  • programmation fonctionnelle
  • ...

On peut adopter donc un style de programmation fonctionnelle avec la plupart des langages. Les carcactéristiques des langages peuvent rendre cela plus ou moins facile (voir obligatoire)

One to rule them all

Mais alors, c'est quoi un langage fonctionnel ?

Il n'y a q'un langage fonctionnel : le ƛ-calcul

Fellowship

Savant Fou
Developpeur fonctionnel
Gamin
Developpeur impératif ou OO
Peace and Love
Photo by Vasilios Muselimis on Unsplash

Statique vs Dynamique

Programation fonctionnelle Typage statique

Typage dynamique

Lisp
1958
Scheme
1975
Racket
1994
Clojure
2007
...

Typage statique

ML
1973
Haskell
1990
OCaml
1996
Scala
2004
...

Programmation fonctionnelle en JS - Part I

Function

Functions en JavaScript

// Function is First-class citizen

function mult(a, b) {
    return a * b;
}

console.log(mult(3, 14));

// variable, ES2015+, TypeScript
// Function is First-class citizen
function mult(a, b) {
    return a * b;
}

console.log(typeof mult); // function
console.log(mult(3, 14));

// Variable
const mult2 = function(a, b) {
    return a * b;
};

// ES2015+
const mult3 = (a, b) => a * b;

// TypeScript
const mult4 = (a: number, b: number): number => a * b;

    Effets de bord

    let sum = 0;
    
    [1, 2, 3, 4, 5]
        .forEach(elt => {
            sum += elt
        });
    
    console.log({sum});
    • ⚠️ Évitez les fonctions avec effet de bord !
    • C'est un nid à bugs.
    • => Évitez les fonctions qui retournent void, ou qui n'ont pas de paramètres.

    Function

    Functions sans effet de bord

    let sum = 0;
    
    [1, 2, 3, 4, 5]
        .forEach(elt => {
            sum += elt
        });
    
    console.log({sum});
    const sum = [1, 2, 3, 4, 5]
        .reduce((acc, elt) => acc + elt);
    
    console.log({ sum });

      Instruction vs Expression

      Instruction vs Expression

      const isEven = (n: number): boolean => {
          if (n % 2 === 0) {
              return true;
          } else {
              return false;
          }
      };
      
      [2, 3, 4, 5]
          .forEach(elt => console.log(elt, 'even?', isEven(elt)));
      const isEven = (n: number): boolean =>
          (n % 2 === 0);
      
      [2, 3, 4, 5]
          .forEach(elt => console.log(elt, 'even?', isEven(elt)));

        Mess with state

        The last thing you wanted any programmer to do is mess with internal state even if presented figuratively. Instead, the objects should be presented as sites of higher level behaviors more appropriate for use as dynamic components.

        --Alan Key, The Early History Of Smalltalk

        Immutable

        Immutable

        // let
        let oLet = {a: 1, b: 2};
        // oLet.a = 3;
        
        // Const
        const oConst = {a: 10, b: 20};
        // oConst.a = 30;
        
        // Transparence référentielle ?
        // Modification pour immutable
        
        console.info({oLet});
        console.info({oConst});
        
        
        
        // let & const
        let oLet = {a: 1, b: 2};
        oLet.a = 3;
        
        
        // Transparence référentielle ?
        const oConst = Object.freeze({a: 10, b: 20});
        oConst.a = 30;
        
        // Deconstruction
        const oConst2 = {...oConst, a: 30};
        
        console.info({oLet});
        console.info({oConst});
        console.info({oConst2});

          Immutable

          Comment fait-on avec les structures de données ?

          class List<T> {
              private array: T[];
          
              constructor(elements: T[] = []) {
                  this.array = [... elements];
              }
          
              add(element: T) : List<T> {
                  return new List([...this.array, element]);
              }
          }

          On peut utiliser Immutable.js , mori

          High Order function

          High Order functions

          const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
          type Predicate = (T) => boolean;
          
          const isEven = (n: number): boolean => (n % 2 === 0);
          
          const evenDigits = []; // FIXME
          const oddSquared = [];// FIXME
          
          console.log(evenDigits);
          console.log(oddSquared);
          const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
          type Predicate<T> = (T) => boolean;
          
          
          const isEven = (n: number): boolean => (n % 2 === 0);
          
          const not = <T>(fun: Predicate<T>): Predicate<T> =>
              (n: T) => !fun(n);
          
          const isOdd = not(isEven);
          
          
          const evenDigits = digits.filter(isEven);
          
          const oddSquared = digits
              .filter(isOdd)
              .map(n => n ** 2);
          
          console.log(evenDigits);
          console.log(oddSquared);
          

            for : 🤢

            interface Event { error: boolean; /* ... */}
            
            const funErrors = (events: Event[], size = 10): Event[] =>
                events
                    .filter(evt => evt.error)
                    .slice(0, size);
            
            const notFunErrors = (events: Event[], size = 10): Event[] => {
                const result: Event[] = [];
                for (let i = 0; i < events.length; i++) { // 🤮
                    const evt = events[i];
                    if (evt.error) {
                        result.push(evt);
                    }
                    if (result.length >= size) {
                        return result;
                    }
                }
                return result;
            };

            Composition

            const compose = (g , f) => x => g(f(x));
            
            const composeAll = (...functions) =>
                x => functions.reduceRight((value, fun) => fun(value), x);
            Stage 1 - pipeline operator
            const doubleSay = (str) => str + ', ' + str;
            const capitalize = (str) => str[0].toUpperCase() + str.substring(1);
            const exclaim = (str) => str + '!';
            
            const result1 = exclaim(capitalize(doubleSay('hello')));
            
            const result2 = 'hello'
                |> doubleSay
                |> capitalize
                |> exclaim;

            Lisibilité

            speakers.filter(speaker => speaker.xp > 10 &&
                            speaker.languages.some(lang => lang === 'JavaScript'));
            speakers.filter(speaker => speaker.xp > 10) // is experimented
                    // is love JS
                    .filter(speaker => speaker.languages.some(lang => lang === 'JavaScript'));
            
            const isExperimented = speaker => speaker.xp > 10;
            const isLoveJS = speaker => speaker.languages.some(lang => lang === 'JavaScript');
            
            speakers.filter(isExperimented)
                    .filter(isLoveJS);
            speakers.filter(and(isExperimented, isLoveJS));

            Part I - bilan 1/2

            Part I - bilan 2/2

            • while, do...while, for, for...of, for...in
            • var ou let => ❤️ const
            • o.x = 5; => ❤️ Object.assign ou {...o, x: 5}
            • mutateurs Array: push, pop, shift, unshift, sort, splice, ...
            • ❤️ accesseurs Array: filter, map, slice, reduce ...
            • ✋ Map : clear, delete, set
            • ✋ Set : add, clear, delete

            Programmation fonctionnelle en JS - Part II

            Ce qu'on a appris

            • Function as First Class Citizen
            • High Order Function
            • Referential Transparency
            • Idempotent

            Ce qu'on va voir maintenant

            • Curryfication,
            • Memoïsation,
            • Algebraic Data Type,
            • Pattern Matching,
            • Functor, Monïd, Monad, ...

            Curryfication

            Curryfication

            // type: (number, number) => number
            const mult = (a, b) => a * b;
            
            // idée: identity = mult(1, _)
            
            // double = mult(2, _)
            
            // triple = mult(3, _)
            
            [identity, double, triple]
                .map(fun => fun(42))
                .forEach(x => console.log(x));
            // type: number => number => number
            const mult = a => b => a * b;
            
            const identity = mult(1);
            
            const double = mult(2);
            
            const triple = mult(3);
            
            [identity, double, triple]
                .map(fun => fun(42))
                .forEach(x => console.log(x));

              Curryfication

              Memoïsation

              Memoïsation

              type IntFun = (number) => number;
              
              const stupidMemoizer = (fun: IntFun): IntFun => {
                  const cache: number[] = [];
                  return (n: number) => {
                      const cached = cache[n];
                      if (typeof cached === 'number') {
                          return cached;
                      }
                      return (cache[n] = fun.call(null, n));
                  }
              };
              
              const fibonacci: IntFun = n => {
                  switch (n) {
                      case 1 :
                          return 1;
                      case 2 :
                          return 1;
                      default:
                          return fibonacci(n - 2) + fibonacci(n - 1);
                  }
              };
              
              console.log('fibonacci(15)', fibonacci(15));
              type IntFun = (number) => number;
              
              const stupidMemoizer = (fun: IntFun): IntFun => {
                  const cache: number[] = [];
                  return (n: number) => {
                      const cached = cache[n];
                      if (typeof cached === 'number') {
                          return cached;
                      }
                      return (cache[n] = fun.call(null, n));
                  }
              };
              
              const fibonacci: IntFun = stupidMemoizer(n => {
                  switch (n) {
                      case 1 :
                          return 1;
                      case 2 :
                          return 1;
                      default:
                          return fibonacci(n - 2) + fibonacci(n - 1);
                  }
              });
              
              console.log('fibonacci(15)', fibonacci(15));

                Memoïsation

                • _.memoize
                • 💎 purtée => on peut mettre en cache !

                npm search

                Algebraic Data Type

                type schoolPerson = Teacher
                                  | Director
                                  | Student(string);

                😞 les enum ou les type union de TypeScript ne sont pas des ADT.

                adt.js

                Pattern Matching

                let greeting = stranger =>
                  switch (stranger) {
                  | Teacher => "Hey professor!"
                  | Director => "Hello director."
                  | Student("Richard") => "Still here Ricky?"
                  | Student(anyOtherName) => "Hey, " ++ anyOtherName ++ "."
                  };
                const getLength = vector => {
                    match (vector) {
                        when { x, y, z } ~> return Math.sqrt(x ** 2 + y ** 2 + z ** 2)
                        when { x, y } ~> return Math.sqrt(x ** 2 + y ** 2)
                        when [...etc] ~> return vector.length
                    }
                }
                getLength({x: 1, y: 2, z: 3}) // 3.74165

                🔮 Stage 0 - ECMAScript Pattern Matching

                Déconstruction

                const myPoint = { x: 14, y: 3 };
                const {x, y} = myPoint; // x === 14, y === 3
                
                const tab = [1, 2, 3, 4];
                const [head, ...tail] = tab; // head === 1, tail === [ 2, 3, 4]

                M-word

                A monad is just a monoïd in the category of endofunctors, what's the problem?

                --🤡

                Functor

                Généralisation aux catégories de la notion de morphisme.

                --😂
                interface Functor<A> {
                    map(mapper: (A) => B): Functor<B>;
                }
                Fantasy Land Functor
                interface EndoFunctor<V> {
                    map(mapper: (V) => V): Functor<V>;
                }

                Monoïd

                C'est un magma associatif et unifère, c'est-à-dire un demi-groupe unifère.

                --🤣
                interface SemiGroup {
                    concat: (SemiGroup) => SemiGroup;
                    // this.concat(x.concat(y)) = this.concat(x).concat(y)
                }
                interface Monoid extends SemiGroup {
                    empty: Monoid;
                    // monoid.concat(empty) = monoid, empty.concat(monoid) = monoid
                }

                Fantasy Land Monoid , Fantasy Land Semigroup

                Monade

                Monade pour les humains

                J'ai toujours pas compris !

                --😳, 🤯
                • C'est un objet 🌯
                • qui a des méthodes simples comme par exemple map ou flatMap
                • qui doivent respectées des règles (axioms)
                • ce qui garenti une haute composabilité.
                • Option<V>, Either<A,B>, Result<S,E>, Future<V>, ...

                Monades en JS

                Part II - bilan

                • langage souple permet pas mal de manipulation
                • manque flatMap dans les api
                • on peut utiliser Ramda, monet.js, Folktale, ...
                • Ne pas se sentir exclu de la FP à cause du jargon !

                Remaques sur la performance

                Quoi ?

                Performance en quoi ?

                • temps d'éxécution (mimimum, maximun, moyen, première exécution) ?
                • consomation de mémoire ?
                • consomation d'énergie ?
                • ...

                Règles

                Douter de toutes les mythes et légendes

                • on fait aux bonnes structures de données (Data oriented design)
                • on évite les IO (disque, résau), c'est l'occasion de faire de la FRP
                • le code doit être bien testé
                • on priviligie la lisibilité du code à une (hypothétique) optimisation de performance
                • mettre en cache n'est pas toujours la bonne solution

                Si besoin...

                tout les leviers sont bon, y compris le langage

                • on définit le seuil désiré
                • on effectue des mesures
                • on isole la zone à optimiser (la plus petite possible)
                • on commente pourquoi on n'a perdu de la lisibilité
                • on suit l'évolution des performances

                Conclusion

                🦄 ou 💩 ?

                • 🌾 les bases sont présentes.
                • 👗 écosystème dans ce domaine, plutôt à la mode
                • 😭 flatMap
                • des structures lazy (comme les Stream de Scala)

                Bibliothèques

                Alternatives

                Intéret du JS

                • 🗣 expressivité
                • 🤸‍♀️ souplesse
                • 💰 ecosystème
                • 🦎 évolution dans le bon sens

                Codons en fonctionel

                • 🍼 plus simple
                • ✅ Plus facile à tester
                • 🐛 moins de bugs
                • 🦎 plus évolutif
                • ♻️ applicable sur tous les (bon) langages
                • 🎓 apprendre

                Valeur

                La valeur principale d'un programme est dans ce qu'il réalise, pas dans son style.

                Mais en temps qu'artisan développeur, j'accorde de l'importance au style.

                Liens

                Questions

                Questions ?

                Quizz

                Question 1

                Quel est le prénom de Haskell Curry ?
                A. Haskell
                C. Brooks
                B. Curry
                D. Obi-Wan
                Réponse A

                Question 2

                Combien de doigts à un paresseux à deux doigts ?
                A. deux
                B. quarante-deux
                C. dix
                D. huit
                Réponse C

                Question 3

                Qui a dit "La récurcivité c'est le GOTO de la programation fonctionelle" ?
                A. Moses Schönfinkel
                B. Erik Meijer
                C. Edsger Dijkstra
                D. Donald Duck
                Réponse B

                Question 4

                La fonction () => 42
                A. a un effet de bord
                B. est idempotent
                C. n'est pas currifiable
                D. est une fonction d'ordre supérieur
                Réponse B

                Question 5

                Avec la méthode reduce, je faire facilement?
                A. un équivalent de flatMap
                B. le min (ou max) d'un tableau
                C. le café
                D. toutes les réponses sont bonnes
                Réponse D