3 points par GN⁺ 2025-11-01 | 1 commentaires | Partager sur WhatsApp
  • John Carmack a partagé son point de vue personnel sur l’usage des variables mutables (mutable variables)
  • Il explique qu’en utilisant Python, il a eu tendance à négliger le principe de la “single assignment”, et qu’il faut selon lui s’en méfier consciemment
  • Il souligne qu’en dehors des calculs itératifs dans les boucles, il faut éviter de réaffecter ou de mettre à jour les variables
  • Lorsque toutes les étapes intermédiaires du calcul sont conservées, cela aide au débogage et permet d’éviter qu’une ancienne valeur soit utilisée involontairement lors du déplacement d’un bloc de code
  • Il explique qu’en C/C++, c’est une bonne habitude de déclarer presque toutes les variables en const dès leur initialisation
  • Enfin, il insiste sur le fait qu’il aimerait que mutable soit un mot-clé, afin que l’immuabilité devienne la valeur par défaut

1 commentaires

 
GN⁺ 2025-11-01
Avis Hacker News
  • Après avoir utilisé Clojure pendant 2 ans, j’ai constaté qu’il était vraiment difficile d’expliquer à d’autres développeurs la clarté apportée par l’immutabilité
    Les personnes habituées à penser en termes d’effets produits via des changements d’état ont du mal à le comprendre tant qu’elles ne l’ont pas expérimenté directement

    • Modifier une variable crée des dépendances d’ordre implicites
      Par exemple, si on écrit x = 7; x = x + 3; x = x / 2, changer l’ordre ne provoque pas d’erreur mais donne un résultat différent
      En revanche, si on utilise de nouvelles variables comme x1, x2, un mauvais ordre provoque une erreur et met clairement le problème en évidence
      Au fond, l’affectation unique (single assignment) est une manière d’exprimer explicitement ces dépendances
    • J’ai eu une expérience similaire avec Scheme
      Même en expliquant à des collègues qui n’avaient jamais utilisé de langage fonctionnel à quel point une approche centrée sur les fonctions est facile à tester et propre, ça ne leur parlait pas vraiment
      En Python, il est difficile d’écrire un style fonctionnel de manière lisible, et j’ai l’impression que JS s’en sort mieux
      Au final, seuls les développeurs curieux essaient des langages comme Clojure
    • Si l’on part de données immuables et de fonctions pures, on peut traiter une fonction comme une véritable boîte noire
      La fonction n’a pas besoin de connaître l’état externe, et l’extérieur n’a pas besoin de connaître l’intérieur de la fonction
      Sans connaître l’état global du programme, on peut tester ou déboguer indépendamment une fonction précise
    • Opposer simplement immutabilité et mutabilité, c’est éviter la complexité
      Il est intéressant de voir que la communauté Haskell finit par tenter de réinventer la mutabilité à l’intérieur du système de types
      L’essentiel est de contrôler les effets de bord au moindre coût
    • Clojure a été le langage le plus marquant que j’aie appris
      Haskell avait une barrière d’entrée élevée à cause de son système de types, et F# était trop conciliant, au point qu’on finissait par coder avec une syntaxe de C#
      Grâce à l’homoiconicité de Clojure et à la puissance de ses structures de données, l’idée de « travailler avec des valeurs » m’est apparue clairement pour la première fois
      Je ne l’utiliserai probablement pas dans un cadre professionnel, mais je le recommande vivement à toute personne n’ayant ni expérience en langage fonctionnel ni avec Lisp
  • J’aimerais que les variables soient immuables par défaut et que tout soit une expression
    Mais dans la réalité, en tant que développeur Clojure, je subis l’invasion de Python

    • Je suis moi aussi développeur Python, mais je n’utilise Clojure que pour des projets personnels
      Maintenant, je subis l’invasion de TypeScript, donc je compatis
    • Après avoir appris Rust, j’ai compris qu’un langage n’avait pas besoin d’être purement fonctionnel pour que tout soit une expression
      Cette approche est vraiment utile pour limiter la portée des modifications
    • Clojure est toujours plus rapide que Python, c’est déjà ça
    • Vous êtes simplement quelqu’un qui utilise Clojure, pas obligé de vous définir comme un « programmeur Clojure »
      Pas besoin de se laisser entraîner dans des guerres de chapelle entre langages
      À l’ère des gains de productivité, ces frontières n’ont plus vraiment de sens
      Je recommande l’article Don’t Call Yourself a Programmer
  • J’essaie de minimiser les réaffectations de variables, mais j’utilise parfois le masquage de variables
    J’aime bien les motifs comme result = result.process() parce qu’ils sont concis

    • L’exemple est abstrait, mais dans la plupart des cas on peut donner un nom clair à chaque étape
    • Ce genre de motif peut provoquer des bugs de sécurité
      Par exemple, si process() est une fonction de validation, on peut ne plus savoir clairement à quel moment une valeur a été traitée
      Il vaut donc mieux distinguer explicitement les états par leur nom
    • En style fonctionnel, on peut résoudre cela par du chaînage de fonctions sans variable intermédiaire
      Exemple : result = x |> foo |> bar |> baz ou (-> x foo bar baz)
    • « result.process(), d’accord, mais quel result et quel process au juste ? »
      La personne qui lira le code plus tard risque d’être perdue
    • Si c’est déjà un résultat (result), le retraiter (process) paraît logiquement étrange
  • Le terme même de « variable » m’a toujours dérangé
    Si c’est immuable, pourquoi appeler ça une variable ?

    • Une variable ne change pas pendant l’exécution, mais sa valeur peut différer d’un appel à l’autre
      En Rust, il faut l’indiquer explicitement avec mut pour pouvoir la modifier
      En C, au contraire, il faut passer par le préprocesseur pour créer des constantes, ce qui prête à confusion
    • L’argument x d’une fonction reçoit une valeur différente à chaque appel, c’est donc en soi une valeur variable
      On peut donc parler de variable même sans réaffectation
    • En mathématiques aussi, une variable est un symbole représentant une valeur arbitraire, et non un objet spécifique
      La programmation a repris ce concept tel quel
    • Au final, une variable est « variable » parce que sa valeur peut changer d’une exécution à l’autre
      Une constante, elle, a la même valeur dans toutes les exécutions
      Voir Variable (mathematics)
    • Le mot « variable » est utilisé non pas tant au sens de changement dans le temps qu’au sens de variation selon le contexte
  • Ce serait bien que l’IDE indique visuellement si une variable est modifiée
    Par exemple en marquant légèrement les variables modifiées

    • Dans IntelliJ, les variables réaffectées sont soulignées, et au survol de la souris un indice « Reassigned local variable » s’affiche
      Utiliser autant de final que possible rend le code plus lisible et plus facile à maintenir
    • Mais je pense qu’un opt-in explicite vaut mieux qu’une inférence automatique
      L’IDE devrait avertir, et n’autoriser la modification que lorsqu’elle est vraiment nécessaire
      Comme dans la réflexion de Rich Hickey sur set vs list, il faut choisir des structures qui expriment clairement le sens
    • Swift détecte via le compilateur si une variable est modifiée, et suggère de la remplacer par une constante quand la modification est inutile
    • Les IDE JetBrains ont déjà une fonction pour trouver les emplacements de lecture/écriture d’une variable
    • Si quelqu’un créait un linter pour ça, « mutalator » serait un bon nom
  • J’ai autrefois travaillé sur un projet où l’immutabilité était appliquée strictement pour assurer la sécurité des threads
    Grâce à cela, le code était plus facile à lire et il était plus simple de suivre ce qui pouvait changer
    Depuis, je suis devenu un grand fan de l’immutabilité

    • Dans ce cas, je recommanderais vraiment d’essayer Rust
  • Après avoir travaillé sur une grosse codebase Haskell puis être revenu au C, je me suis dit qu’il vaudrait mieux que l’immutabilité soit la norme
    const ne suffit pas

    • En C, on ne peut en réalité pas modifier directement, seulement réaffecter des valeurs
      Pour modifier, il faut utiliser des pointeurs, et en C++, un simple appel de fonction peut aussi changer les arguments, ce qui manque de transparence
    • Je me demande si le comportement par défaut de Rust fournit une immutabilité suffisamment sûre
  • Je suis d’accord avec l’idée que « déclarer presque toutes les variables en const est une bonne pratique »
    Il est logique de mentionner Rust

  • J’imagine une syntaxe où l’immutabilité est la règle, et où le mutable n’est autorisé qu’à l’intérieur d’un bloc précis
    Par exemple comme un bloc with en Python

    with mutable(x, items):
        x = 3
        items.append(4)
    

    Une fois sorti du bloc, tout redeviendrait immuable

    • C’est en fait très proche du concept de mutable borrow
      Le borrow checker de Rust montre à quel point cette idée est complexe
    • Sans vérification des emprunts, des références pourraient subsister hors du bloc et permettre des modifications
  • Il y a ce dicton : « State is the enemy »
    Plus l’état augmente, plus les conditions à tester augmentent de manière exponentielle
    L’immutabilité est une façon d’éviter cette explosion des états