POO vs Fonctionnel — 00 — Deux paradigmes, une seule question

Histoire et philosophie de la POO et de la programmation fonctionnelle. Pourquoi ce débat existe, ce qu'il cache vraiment, et pourquoi TypeScript et Python ont tranché.

00 — Deux paradigmes, une seule question

Ce que tu vas apprendre

  • L'origine historique des deux paradigmes
  • Ce que chacun propose comme modèle mental
  • Pourquoi le débat "POO vs Fonctionnel" est souvent mal posé
  • L'état des choses en TypeScript et Python en 2026

Deux paradigmes nés à la même époque

La programmation orientée objet et la programmation fonctionnelle ont émergé dans les années 1950-1970 — bien avant Java, Python ou TypeScript.

Côté fonctionnel : John McCarthy invente Lisp en 1958 au MIT. Lisp repose sur une idée simple : tout est expression, les fonctions sont des valeurs comme les autres, et l'état mutable est à éviter. ML sort en 1973 (Robin Milner, Edinburgh), suivi de Haskell en 1990.

Côté objet : Ole-Johan Dahl et Kristen Nygaard inventent Simula 67 à Oslo pour simuler des systèmes physiques — des entités avec un état propre qui interagissent. Alan Kay s'en inspire pour créer Smalltalk au Xerox PARC dans les années 1970. C++ popularise les concepts dans les années 1980, Java les impose dans les entreprises à partir de 1995.

Les deux paradigmes répondent à la même question fondamentale : comment organiser du code pour qu'il soit compréhensible, réutilisable et correct ? Ils y répondent différemment.

Lisp (FP) Simula (OOP) Haskell
1958 1967 1990

Ce que chaque paradigme propose

La POO : organiser autour des objets

La POO regroupe données et comportements dans une même unité : l'objet. Un User a un nom, un email, et des méthodes pour les modifier. Un Order a un statut et des méthodes pour le faire progresser.

Le modèle mental est celui du monde physique : des entités qui ont un état et interagissent par envoi de messages. Alan Kay, l'inventeur de Smalltalk, insistait d'ailleurs sur les messages plutôt que sur les classes — une nuance souvent perdue dans les implémentations mainstream.

"I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages." — Alan Kay, The Early History of Smalltalk (1993)

Le fonctionnel : organiser autour des transformations

La programmation fonctionnelle traite le code comme des transformations de données. L'entrée entre, une sortie sort, rien d'autre ne se passe. Pas d'état partagé, pas d'effets de bord cachés.

Le modèle mental est celui des mathématiques : une fonction est une relation entre un ensemble d'entrées et un ensemble de sorties. f(x) = y — toujours. Pas "y la plupart du temps sauf si quelqu'un a modifié this.state entre deux appels."

John Hughes, dans son article fondateur de 1990, pose la question directement :

"Functional programs contain no assignment statements, so variables, once given a value, never change. More generally, functional programs contain no side-effects at all." — John Hughes, Why Functional Programming Matters (1990)


Le vrai désaccord : comment gérer l'état

Les deux paradigmes divergent sur un point central : comment gérer l'état et les changements au fil du temps.

La POO encapsule l'état dans des objets et le modifie via des méthodes. C'est intuitif — un compte bancaire a un solde qui change.

Le fonctionnel évite la mutation. Pour "modifier" une valeur, tu crées une nouvelle valeur. Un compte bancaire ne "change" pas son solde — il retourne un nouveau compte avec un solde différent.

typescript// POO — état mutable
class BankAccount {
  private balance: number;

  constructor(initialBalance: number) {
    this.balance = initialBalance;
  }

  deposit(amount: number): void {
    this.balance += amount; // Mutation
  }

  getBalance(): number {
    return this.balance;
  }
}

// FP — données immuables
type Account = { balance: number };

function deposit(account: Account, amount: number): Account {
  return { balance: account.balance + amount }; // Nouveau objet
}

const account = { balance: 1000 };
const after = deposit(account, 500);

console.log(account.balance); // 1000 — inchangé
console.log(after.balance);   // 1500 — nouvel objet
python# POO — état mutable
class BankAccount:
    def __init__(self, initial_balance: int) -> None:
        self._balance = initial_balance

    def deposit(self, amount: int) -> None:
        self._balance += amount

    @property
    def balance(self) -> int:
        return self._balance

# FP — données immuables
from dataclasses import dataclass

@dataclass(frozen=True)
class Account:
    balance: int

def deposit(account: Account, amount: int) -> Account:
    return Account(balance=account.balance + amount)

account = Account(balance=1000)
after = deposit(account, 500)

print(account.balance)  # 1000 — inchangé
print(after.balance)    # 1500 — nouvel objet

Pourquoi le débat est souvent mal posé

Le débat "POO vs Fonctionnel" est réel, mais il est souvent posé comme s'il fallait choisir un camp. Ce n'est pas le cas.

Les deux paradigmes résolvent des problèmes différents.

La POO est efficace pour modéliser des domaines avec des entités qui ont un cycle de vie — utilisateurs, commandes, sessions. L'encapsulation protège l'intégrité de l'état.

Le fonctionnel est efficace pour transformer des données — traiter des collections, parser du JSON, construire des pipelines de transformation. La pureté rend le code testable et prévisible.

Peter Van Roy et Seif Haridi, dans leur étude des paradigmes de programmation, identifient plus de vingt paradigmes distincts et montrent que chacun est adapté à certains types de problèmes — aucun n'est universel.

"Each paradigm has its place. The skill is knowing which to use when." — Peter Van Roy & Seif Haridi, Concepts, Techniques, and Models of Computer Programming (2004)


L'état des choses en 2026

Les langages modernes ont tranché : ils supportent les deux.

TypeScript a des classes (ES6+) et des fonctions de première classe. Tu peux écrire du code purement fonctionnel avec map, filter, reduce et des fonctions fléchées, ou du code orienté objet avec des classes et des décorateurs. Les deux styles coexistent dans la même codebase.

Python est multi-paradigme par conception. functools, itertools, les list comprehensions et les générateurs viennent du fonctionnel. Les classes, l'héritage et les décorateurs viennent de la POO. Guido van Rossum a ajouté des fonctionnalités fonctionnelles (lambda, map, filter) tout en maintenant Python comme un langage "objet avant tout."

Kotlin, Scala, F#, Rust font de même — certains penchent plus vers le fonctionnel (Scala, F#), d'autres vers l'objet (Kotlin), mais tous supportent les deux.

La question n'est plus "lequel choisir ?" mais "lequel est le mieux adapté à ce problème précis ?"


Ce que cette série va couvrir

Article Contenu
00 — Introduction Histoire, philosophie, état du débat
01 — Piliers de la POO Encapsulation, héritage, polymorphisme, abstraction
02 — Piliers du fonctionnel Pureté, immutabilité, composition, HOF, currying
03 — Comparaison directe Le même problème, deux solutions
04 — L'approche hybride TypeScript et Python : les deux paradigmes ensemble
05 — Patterns fonctionnels dans un projet OOP Intégrer le FP dans un code existant

Étape suivante : 01 — Les piliers de la POO


Sources

  • Hughes, J. (1990). Why Functional Programming Matters. The Computer Journal, 32(2), 98-107.
  • Kay, A. (1993). The Early History of Smalltalk. ACM SIGPLAN Notices, 28(3), 69-95.
  • Van Roy, P., & Haridi, S. (2004). Concepts, Techniques, and Models of Computer Programming. MIT Press.
  • Backus, J. (1978). Can Programming Be Liberated from the von Neumann Style? Communications of the ACM, 21(8), 613-641.
  • McCarthy, J. (1960). Recursive Functions of Symbolic Expressions and Their Computation by Machine. Communications of the ACM, 3(4), 184-195.

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.