Skip to content

Chapitre 01 : Que faisons-nous exactement ?

Présentation

Bonjour ! Je suis le professeur Franklin Frisby. Ravi de faire votre connaissance. Nous allons passer quelque temps ensemble, car je suis censé vous enseigner les bases de la programmation fonctionnelle. Assez parlé de moi, parlons de vous. J'espère que vous avez une connaissance minimale de JavaScript, une faible expérience en programmation orientée objet, et que vous vous considérez comme un programmeur de la classe ouvrière. Pas besoin de doctorat en entomologie - juste de savoir traquer et éliminer des bugs.

Je ne présupposerai aucune connaissance en programmation fonctionnelle, car nous savons tous ce qu'engendrent les présomptions. Cependant, je m'attends à ce que vous ayez expérimenté les écueils des états mutants, des effets secondaires non contrôlés et des designs anarchiques. Maintenant que les présentations sont faites, commençons.

L'objectif de ce chapitre est de vous faire ressentir l'essence des programmes fonctionnels. Pour comprendre les chapitres suivants, nous devons saisir ce qui rend un programme fonctionnel. Sans cela, nous écririons du code chaotique en évitant les objets à tout prix - une entreprise vouée à l'échec. Nous avons besoin d'une cible claire pour orienter notre code, d'une boussole céleste pour naviguer en eaux troubles.

Il existe des principes généraux en programmation - des acronymes sacro-saints qui nous guident dans les ténèbres : DRY (ne pas se répéter), YAGNI (vous n'en aurez pas besoin), faible couplage/haute cohésion, principe de moindre surprise, responsabilité unique, etc.

Je ne vous infligerai pas la liste exhaustive des principes existants... L'important est qu'ils restent valables en contexte fonctionnel, bien que périphériques à notre but ultime. Avant d'aller plus loin, je veux que vous saisissiez notre intention profonde lorsque nous codons : notre Xanadu fonctionnel.

Une brève rencontre

Commençons par un exemple déraisonnable. Voici une simulation de mouettes. Quand deux groupes se rejoignent, ils fusionnent. Quand ils se reproduisent, leur nombre augmente selon leurs partenaires. Attention, ceci n'est pas un bon code orienté objet - il illustre les dangers d'une approche mutationnelle moderne. Observez :

js
class Flock {
  constructor(n) {
    this.seagulls = n;
  }

  conjoin(other) {
    this.seagulls += other.seagulls;
    return this;
  }

  breed(other) {
    this.seagulls = this.seagulls * other.seagulls;
    return this;
  }
}

const flockA = new Flock(4);
const flockB = new Flock(2);
const flockC = new Flock(0);
const result = flockA
  .conjoin(flockC)
  .breed(flockB)
  .conjoin(flockA.breed(flockB))
  .seagulls;
// 32

Qui donc créerait une telle abomination ? Suivre l'état interne mutant relève de l'exploit. Et pire, le résultat est erroné ! Cela devrait être 16, mais flockA se trouve altéré définitivement. Pauvre flockA. C'est l'anarchie informatique ! De l'arithmétique sauvage !

Si ce programme vous semble obscur, rassurez-vous : moi aussi. Retenez surtout que les états mutants restent complexes à suivre, même dans un cas simple.

Recommençons avec une approche fonctionnelle :

js
const conjoin = (flockX, flockY) => flockX + flockY;
const breed = (flockX, flockY) => flockX * flockY;

const flockA = 4;
const flockB = 2;
const flockC = 0;
const result =
    conjoin(breed(flockB, conjoin(flockA, flockC)), breed(flockA, flockB));
// 16

Cette fois, le résultat est correct. Avec moins de code. L'imbrication de fonctions est un peu confuse... (nous corrigerons cela au chapitre 5). C'est mieux, mais approfondissons. Nommer les choses correctement éclaire le code. En examinant nos fonctions, on découvre qu'elles correspondent simplement à l'addition (conjoin) et la multiplication (breed).

Rien de spécial à part leurs noms. Renommons-les add et multiply pour révéler leur vraie nature :

js
const add = (x, y) => x + y;
const multiply = (x, y) => x * y;

const flockA = 4;
const flockB = 2;
const flockC = 0;
const result =
    add(multiply(flockB, add(flockA, flockC)), multiply(flockA, flockB));
// 16

Ainsi s'éclaire la sagesse ancestrale :

js
// associative
add(add(x, y), z) === add(x, add(y, z));

// commutative
add(x, y) === add(y, x);

// identity
add(x, 0) === x;

// distributive
multiply(x, add(y,z)) === add(multiply(x, y), multiply(x, z));

Ah, ces bonnes vieilles propriétés mathématiques ! Ne vous inquiétez pas si les termes associativité et commutativité vous semblent lointains. Pour beaucoup, ces lois arithmétiques datent. Utilisons-les pour simplifier notre exemple.

js
// Original line
add(multiply(flockB, add(flockA, flockC)), multiply(flockA, flockB));

// Apply the identity property to remove the extra add
// (add(flockA, flockC) == flockA)
add(multiply(flockB, flockA), multiply(flockA, flockB));

// Apply distributive property to achieve our result
multiply(flockB, add(flockA, flockA));

Brillant ! Aucun code personnalisé n'était nécessaire. Les définitions d'add et multiply sont ici pour complétude, mais en pratique, utilisez les fonctions standard.

Vous penserez peut-être « Quel exemple mathématique simpliste ! » ou « Les vrais programmes sont plus complexes. ». J'ai choisi cet exemple car les opérations de base sont connues, illustrant clairement l'utilité des mathématiques.

Ne désespérez pas : ce livre mêlera théorie des catégories, théorie des ensembles et lambda-calcul pour des exemples réalistes, tout en restant accessibles. Aucun doctorat requis - ce sera aussi naturel qu'utiliser un framework classique.

Vous serez surpris de pouvoir développer des applications complètes selon ce paradigme fonctionnel. Des programmes solides, concis et cohérents. Qui évitent de réinventer la roue. L'anarchie convient aux criminels, mais ici nous suivrons les lois mathématiques.

Nous utiliserons une théorie où chaque pièce s'emboîte harmonieusement. Nous exprimerons nos problèmes via des composants génériques et combinables, exploiterons leurs propriétés à notre avantage. Cela demande plus de discipline que l'approche impérative « tout est permis » (nous définirons « impératif » plus tard). Les bénéfices vous stupéfieront.

Nous entrevoyons notre étoile polaire fonctionnelle, mais quelques concepts clés doivent être acquis avant le voyage.

Chapitre 02 : Fonctions de première classe