Je ne sais pas si tu m'a classé dans les anti abstraction. Tout est une abstraction et ne pas décider de quel niveau d'abstraction on utilise ne veut pas dire que l'on en utilise pas juste que ce sera probablement mal fait.
Bref, quand tu dis que l'abstraction se fait au détriment de l'évolution ou de l'utilisabilité, je pense personnellement qu'à minima, on ne parle pas de la même chose car la création d'une abstraction n'à qu'un seul but détecter une situation problématique récurrente bien cadrée et déterminée pour proposer une solution ouverte et non enfermante.
Je pense qu'on parle de la même chose, mais qu'on ne le décrit pas de la même façon.
Je vais donner des exemples pour que ce que soit plus clair.
Un typage "trop" large
En java, la bibliothèque de collection décrit ArrayList ← List ← Collection ← Iterable.
L'important n'est pas tant l'utilité du code, mais que le paramètre titi peut ici être typé en Iterable. C'est l'interface la plus ouverte que tu peux faire à cette fonction. Mais tu ne pourra alors pas la rééacrire pour tirer partie des stream de java 8 et plus (ou alors il faut que tu crée ton stream à la main) si tu avais gardé une collection tu pourrais écrire quelque chose comme :
returntotos.stream().anyMatch(this::predicate);
Ce que je vois par là c'est que lorsque tu choisi tes types pour élargir l'utilisabilité, tu peux facilement te retrouver à retirer une propriété de ton contrat d'entrée qui n'a pas d'intérêt dans ton implémentation actuelle mais qui a tout de même son utilité.
L'utilisabilité d'un typage restrictif
L'autre direction c'est le classique des types cachés que tu peux trouver en ocaml par exemple. Tu peux définir un type encrypted qui est une string cachée (c'est à dire un alias sur le type string, mais qui n'est pas vu comme une string dans le système de type) et avoir des fonctions qui ne prennent que des données encrypted. Ça permet d'avoir des garanties supplémentaires, même si en soit ta fonction pourrait très bien prendre n'importe quelle chaîne de caractère.
J'obtiens une exception parce que les List produites par toList() sont immutables. Donc aujourd'hui les développeurs d'openjdk sont coincés avec une interface List qui se considère mutable alors qu'ils aimeraient avoir des list immutables (oui oui c'est moche).
Là je donne des exemples sur des structures de données, mais tu peux avoir la même chose sur ton type générique (si tu es habitué à java : les T extends Bidule).
Des personnes influentes comme "Joel On Software" ont d'ailleurs parlé avec justesse du concept de "Leaky Abstraction" [1] pour dénoncer à l'époque et mettre en garde contre les dérives potentiels d'une utilisation abusive et non maitrisée des abstractions. Mais ce constat s'applique aussi à d'autre pratique par exemple l'optimisation prématurée [2] qui va conduire à des problématiques inutiles et couteuses à résoudre.
Faut que je retrouve j'avais suivi une discussion intéressante sur twitter où quelqu'un donnait un autre terme de "prémature optimization" bien plus clair pour ne pas invalider toute forme de discussion sur les performances.
[^] # Re: Trou de mémoire
Posté par barmic 🦦 . En réponse au lien Go 1.18 Beta : la généricité enfin !. Évalué à 3.
Je ne sais pas si tu m'a classé dans les anti abstraction. Tout est une abstraction et ne pas décider de quel niveau d'abstraction on utilise ne veut pas dire que l'on en utilise pas juste que ce sera probablement mal fait.
Je pense qu'on parle de la même chose, mais qu'on ne le décrit pas de la même façon.
Je vais donner des exemples pour que ce que soit plus clair.
Un typage "trop" large
En java, la bibliothèque de collection décrit ArrayList ← List ← Collection ← Iterable.
Si tu as une fonction comme ceci :
L'important n'est pas tant l'utilité du code, mais que le paramètre
titipeut ici être typé en Iterable. C'est l'interface la plus ouverte que tu peux faire à cette fonction. Mais tu ne pourra alors pas la rééacrire pour tirer partie des stream de java 8 et plus (ou alors il faut que tu crée ton stream à la main) si tu avais gardé une collection tu pourrais écrire quelque chose comme :Ce que je vois par là c'est que lorsque tu choisi tes types pour élargir l'utilisabilité, tu peux facilement te retrouver à retirer une propriété de ton contrat d'entrée qui n'a pas d'intérêt dans ton implémentation actuelle mais qui a tout de même son utilité.
L'utilisabilité d'un typage restrictif
L'autre direction c'est le classique des types cachés que tu peux trouver en ocaml par exemple. Tu peux définir un type
encryptedqui est une string cachée (c'est à dire un alias sur le type string, mais qui n'est pas vu comme une string dans le système de type) et avoir des fonctions qui ne prennent que des données encrypted. Ça permet d'avoir des garanties supplémentaires, même si en soit ta fonction pourrait très bien prendre n'importe quelle chaîne de caractère.Les problèmes d'un typage pas assez restrictif
Si j'écris en java:
J'obtiens une exception parce que les
Listproduites partoList()sont immutables. Donc aujourd'hui les développeurs d'openjdk sont coincés avec une interfaceListqui se considère mutable alors qu'ils aimeraient avoir des list immutables (oui oui c'est moche).Là je donne des exemples sur des structures de données, mais tu peux avoir la même chose sur ton type générique (si tu es habitué à java : les
T extends Bidule).Faut que je retrouve j'avais suivi une discussion intéressante sur twitter où quelqu'un donnait un autre terme de "prémature optimization" bien plus clair pour ne pas invalider toute forme de discussion sur les performances.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll