Share
Embed
Philosophie Fonctionnelle - Bdx.io 2016
Découvrez ce paradigme à travers son modèle de pensée et non ses outils. Une présentation accessible à tous, garantie sans jargon ou concepts complexes pour un usage du fonctionnel au quotidien quelque soit votre langage.
Arnaud LEMAIRE
October 21, 2016
Transcript
48 slides
-
Une valeur qui change en fonction du temps let variable;
variable = "foo"; console.log(variable); variable = "bar"; console.log(variable); "foo" "bar" temps -
Et les problèmes arrivent quand on multiplie les sources de
modifications "foo" "bar" invalid value context A context B -
Très souvent vous avez un gros entrepôt à état pour
toute l’application La base de donnée (on en reparlera…) -
La programmation fonctionnelle supprime le temps de l’équation temps x
valeur = variable valeur = constante -
-Michael Feathers « OO makes code understandable by encapsulating moving
parts. FP makes code understandable by minimizing moving parts » -
valeur accessible « plus tard » = fonction La fonction
n’est qu’une valeur disponible « plus tard » valeur accessible maintenant = constante const hello = "world"; const pow2 = (x) => x ** 2; -
Cela permet notamment l’évaluation paresseuse (lazy evaluation) Au lieu d’assigner
immédiatement un résultat à une constante : const result = heavy_process(some, args); const lazy_result = () => heavy_process(some, args); Celui-ci est placé dans une fonction exécutée au moment opportun (qui peut ne jamais arriver) : -
Fonction et valeur sont donc considérées à égalité L’on peut
manipuler les fonction comme des valeurs = fonctions d’ordre supérieures const pow = x => y => y ** x; const pow3 = pow(3); console.log(pow3(3)); //print 27 -
Fonction et valeur sont donc considérées à égalité Calculée, une
fonction peut être remplacée par une constante = mémoïzation const memoize = (f) => { const memo = {}; return (...args) => (args in memo) ? memo[args] : (memo[args] = f(...args)); }; const add = (x, y) => x + y; const memoized_add = memoize(add); memoized_add(3, 4); -
Constantes et fonctions sont considérés à « équivalence » La
différence se trouve l’immédiateté de l’accès à l’information contenue dans la référence associée -
Il était des fonctions qui partageaient des pointeurs function copyF($fromPath,
$toPath) { $fromHandle = fopen($fromPath, 'r'); $toHandle = fopen($toPath, 'w+'); $contentToCopy = fread( $fromHandle, filesize($fromPath)); fwrite($toHandle, $contentToCopy); } function copyO($fromPath, $toPath) { $from = new SplFileObject($fromPath); $to = new SplFileObject($toPath); $content = $from->fread($from->getSize()); $to->fwrite($content); } -
Une map de fonctions ? const triangle = {
rotate: (d) => (…), move: (x, y) => (…), //... }; -
Un ensemble de fonction qui reçoivent le même paramètre ?
class Triangle: def rotate(self, degrees): //... def move(self, x, y): //... //... -
Une map de fonctions appliquées partiellement sur une même référence
const square = (l, x, y) => { const that = [l, x, y]; const rotate = (self, d) => (…); const move = (self, x, y) => (…); return { rotate: (d) => rotate(that, d), move: (x, y) => move(that, x, y) //... } }; const obj = square(5, 10, 15); obj.rotate(…); -
C’est pourquoi l’objet peut être exprimé en fonctionnel mais pas
l’inverse Un langage fonctionnel pourra toujours exprimer la notion « d’objet », mais un langage objet ne garantie pas la possibilité d’utiliser un style fonctionnel (La plupart des langages possèdent maintenant des constructions fonctionnelles) -
-Douglas Crockford « I'm now thinking I've got two completely
different types of objects: those containing only data, which are things I share with JSON; and things containing only methods, which are things I can share with applications » -
Les objets proposent une interface à des types de données
« complexes » La poo repose sur des structures de données « intelligentes » Le style fonctionnelle utilise très peu de type différents manipulé par des fonctions génériques circle = Circle.new(5, 7, 15) circle.move(3, 10) circle.scale(20) circle = [5, 7, 15] move_to(circle, 3, 10) scale(circle, 20) -
Ce qui permet d’exprimer un algorithme complex qu’en composant des
fonctions existantes Ces fonctions génériques peuvent être composés entre elles move_to_and_scale = -> (circle) { move_to(scale(circle, 20), 3, 10) } -
Un objet est de la donnée sur laquelle l’on va
venir appliquer du comportement La closure est une « inverse » d’objet Une closure est du comportement qui attend de la donnée circle = Circle.new(5, 7, 15) circle.move(3, 10) move_to_parametrized = ->(x, y) { -> (circle) { move_to(circle, x, y) }} circle = [5, 7, 15] move_to_5_15 = move_to_parametrized(5, 15); move_to_5_15(circle) -
Le pattern matching est du polymorphisme sur les types de
données type 'a tree = | Node of 'a tree * 'a * 'a tree | Leaf;; let rec fold_tree f a t = match t with | Leaf -> a | Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);; Le pattern matching permet d’adapter un comportement en se basant sur la structure ou le type de la donnée -
Smart data-types vs smart functions La programmation fonctionnelle possède des
structure de donnée très simple, sur lesquelles des fonctions vont pouvoir adapter leurs comportement -
-Alan Perlis « It is better to have 100 functions
operate on one data structure than 10 functions on 10 data structures » -
En poo les types sont très souvent associés à des
structures de données nommées Les types en Orienté Objet, le typage « nominatif » Il n’y a pas de différence entre la « forme » de la donnée et ses comportements circle = Circle.new(5, 7, 15) circle.move(3, 10) circle.scale(20) Deux types sont égaux si ils portent le même nom -
En programmation fonctionnelle le système de type représente une «
forme » (une structure) de donnée Les types en fonctionnel, le typage structurel Toute fonction acceptant une pair d’entier sera compatible avec ce type type pair_of_ints = { a : int; b : int };; -
Les types algébriques permettent de réaliser des produits et des
sommes de types Les types en fonctionnelle, le typage algébrique type Suit = Spades | Hearts | Clubs | Diamonds type Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace type Card = Rank * Suit type Deck = Card list -
Typage structurel & algébrique En séparant les types des comportements
associés et en permettant la combinaison de type, les langages fonctionnels permettent de représenter plus facilement des situations métiers plus complexes -
Immutabilité partout Eviter les mutations dans son code const constant
= "everywhere"; Même pour les boucles (ça se remplace très bien par le trio : map, reduce, filter) -
Eviter les mutations dans son code Au lieu de changer
l’état d’un objet, retournez un nouvel objet class Circle def initialize r, x, y @r = r @x = x @y = y end def move x, y Circle.new(@r, @x + x, @y + y) end end -
Utilisez des types algébriques Ça existe même en javascript… (tcomb)
const Min = t.refinement( t.String, (s) => s.length > 2); const Max = t.refinement( t.String, (s) => s.length < 5); const MinMax = t.intersection([Min, Max]); MinMax.is('abc'); // => true MinMax.is('a'); // => false MinMax.is('abcde'); // => false -
Ils sont indépendant, conservent leur propre état et communiquent via
des messages immutable Utiliser des acteurs -
Isolez les parties impératives Functional core / Imperative shell Business
domain (functional) Utilisez des objets valeurs pour faire l’échange entre les deux couches -
Considérer son logiciel comme un ensemble de fonctions de transformations
Ensemble de départ Ensemble d’arrivé fonction Type de départ Type d’arrivée