Je le trouve inadapté pour modéliser des concepts issus des langages fonctionnels
Ça ne sert effectivement à rien. Un jour j'ai demandé à quelqu'un de m'expliquer l'UML, au bout de dix minutes je lui ai dit d'arrêter : je n'avais rien à apprendre d'une personne qui ne sait pas structurer logiquement sa pensée de manière adéquate. En revanche j'use abondamment de diagrammes : ceux de la théorie des catégories. Bon après, j'ai une gros rejet de la POO, non du concept d'objet de la POO, mais du paradigme dans lequel tout est objet (à la Java). C'est pour moi un paradigme d'asile d'aliéné, ou l'art de faire de l'algèbre en marchant sur la tête. Je m'explique en reprenant le cas d'étude de l'article.
Qu'un carré, un rectangle ou un cercle soient des formes, il n'y a là aucun doute. Mais cette propriété n'a rien à foutre dans leur définition ! Ce qui est inévitablement le cas si on les définit comme des objets. Personnellement je les définit ainsi :
Ce qui définit chacun des types, ce sont les enregistrements et la propriété qu'ils ont d'avoir une surface se traduit par l'existence d'une fonction area sur chacun de ces types. Après le concept d'un objet, au sens de la POO, c'est le type suprême de toutes les formes, celui dans lequel on peut injecter n'importe quel forme. Ce que ne semble pas avoir compris l'auteur de l'article du lien, puisqu'il ne considère que le type somme de quatre formes possibles, alors qu'il y a aussi n'importe quel polygone, des ellipses...
Et voilà comment on définit, algébriquement, un tel type :
moduletypeShape=sigmoduletypeS=sigtypetvalarea:t->floatendtype'ameth=(moduleSwithtypet='a)(* les valeurs de type `t` sont des formes *)typet(* toute valeur d'un type `'a` qui possède une méthode `area` est une forme *)valmake:'ameth->'a->tvalarea:t->floatend
Je n'ai donné que l'interface (ou API) d'un module qui définit ce genre suprême, sans préciser les détails d'implémentation ("Code should not know about the internals of objects it’s working with" ;-). Pour l'implémentation du type Shape.t je peux prendre aux choix :
un objet : type t = < area : unit -> float >
une clôture : type t = unit -> float
un gadt : type t = Shape : {meth : 'a meth; self : 'a} -> t
et la liste n'est pas exhaustive.
Et c'est là qu'interviennent les diagrammes de la théorie des catégories. Une forme est caractérisée par l'existence d'une fonction area sur son type. Algébriquement cela se traduit par l'étude des structures algébriques ayant la signature Shape.S :
moduletypeS=typetvalarea:t->floatend
On peut faire un graphe orienté de toutes ces structures : chaque nœud du graphe est un module satisfaisant cette signature, et chaque arête est une fonction qui transforme un nœud en un autre (par exemple, la proposition tous les carrés sont des rectangles est représentée par une telle arrête). Mais dans ce graphe (qui, en soit, contient une infinité de nœuds), il y a un nœud particulier T (ou plutôt une famille de nœud). Ce dernier à la propriété que pour tout autre nœud N du graphe il existe une et une seule arrête qui va de N à T. Ce nœud T est le type Top de toutes ces structures algébriques, celui qui les contient toutes (les carrés, les rectangles, les cercles...). En réalité, il y a une infinité de tels nœuds Top, mais ils sont tous équivalents et ne diffèrent que par leurs détails d'implémentation (j'en ai donné 3 possibles au-dessus). La fonction centrale de cette structure c'est de faire du dynamic dispacth ou du polymorphisme ad-hoc : il applique la fonction area adéquate à la forme qu'il contient. Mais la stratégie à base d'objets et de vtable n'est qu'une solution parmi d'autre pour faire cela. On trouve par exemple, dans des langages existant :
les modules (ou les objets) en OCaml ;
les objets dans tout langage orienté POO ;
les interface en Golang ;
les type class en Haskell ;
les template en C++ (c'est l'équivalent des modules OCaml) ;
les traits en Rust ;
et bien d'autres encore.
Mais pour définir une fonction sur une liste de formes, ou sur une forme quelconque, c'est-à-dire pour faire du polymorphisme ad-hoc, il n'est absolument pas nécessaire de les injecter dans le type Top (qui est le type des objets de la POO), et c'est ce que je reproche à la POO : elle réduit la catégorie à son seul objet terminal (ce type Top), ce qui est une manière plus que bancal de faire de l'algèbre. D'autant que de nombreuses catégories de structures algébriques n'ont pas d'objet terminal, à commencer par toutes celles qui ont des opérateurs binaires (addition, multiplication), où il devient alors vraiment ridicule de suivre le paradigme du tout objet.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: l'héritage et les exemples pourris
Posté par kantien . En réponse au lien « Clean code » : performances lamentables. Évalué à 4.
Ça ne sert effectivement à rien. Un jour j'ai demandé à quelqu'un de m'expliquer l'UML, au bout de dix minutes je lui ai dit d'arrêter : je n'avais rien à apprendre d'une personne qui ne sait pas structurer logiquement sa pensée de manière adéquate. En revanche j'use abondamment de diagrammes : ceux de la théorie des catégories. Bon après, j'ai une gros rejet de la POO, non du concept d'objet de la POO, mais du paradigme dans lequel tout est objet (à la Java). C'est pour moi un paradigme d'asile d'aliéné, ou l'art de faire de l'algèbre en marchant sur la tête. Je m'explique en reprenant le cas d'étude de l'article.
Qu'un carré, un rectangle ou un cercle soient des formes, il n'y a là aucun doute. Mais cette propriété n'a rien à foutre dans leur définition ! Ce qui est inévitablement le cas si on les définit comme des objets. Personnellement je les définit ainsi :
Ce qui définit chacun des types, ce sont les enregistrements et la propriété qu'ils ont d'avoir une surface se traduit par l'existence d'une fonction
areasur chacun de ces types. Après le concept d'un objet, au sens de la POO, c'est le type suprême de toutes les formes, celui dans lequel on peut injecter n'importe quel forme. Ce que ne semble pas avoir compris l'auteur de l'article du lien, puisqu'il ne considère que le type somme de quatre formes possibles, alors qu'il y a aussi n'importe quel polygone, des ellipses...Et voilà comment on définit, algébriquement, un tel type :
Je n'ai donné que l'interface (ou API) d'un module qui définit ce genre suprême, sans préciser les détails d'implémentation ("Code should not know about the internals of objects it’s working with" ;-). Pour l'implémentation du type
Shape.tje peux prendre aux choix :et la liste n'est pas exhaustive.
Et c'est là qu'interviennent les diagrammes de la théorie des catégories. Une forme est caractérisée par l'existence d'une fonction
areasur son type. Algébriquement cela se traduit par l'étude des structures algébriques ayant la signatureShape.S:On peut faire un graphe orienté de toutes ces structures : chaque nœud du graphe est un module satisfaisant cette signature, et chaque arête est une fonction qui transforme un nœud en un autre (par exemple, la proposition tous les carrés sont des rectangles est représentée par une telle arrête). Mais dans ce graphe (qui, en soit, contient une infinité de nœuds), il y a un nœud particulier T (ou plutôt une famille de nœud). Ce dernier à la propriété que pour tout autre nœud N du graphe il existe une et une seule arrête qui va de N à T. Ce nœud T est le type
Topde toutes ces structures algébriques, celui qui les contient toutes (les carrés, les rectangles, les cercles...). En réalité, il y a une infinité de tels nœuds Top, mais ils sont tous équivalents et ne diffèrent que par leurs détails d'implémentation (j'en ai donné 3 possibles au-dessus). La fonction centrale de cette structure c'est de faire du dynamic dispacth ou du polymorphisme ad-hoc : il applique la fonctionareaadéquate à la forme qu'il contient. Mais la stratégie à base d'objets et de vtable n'est qu'une solution parmi d'autre pour faire cela. On trouve par exemple, dans des langages existant :Mais pour définir une fonction sur une liste de formes, ou sur une forme quelconque, c'est-à-dire pour faire du polymorphisme ad-hoc, il n'est absolument pas nécessaire de les injecter dans le type
Top(qui est le type des objets de la POO), et c'est ce que je reproche à la POO : elle réduit la catégorie à son seul objet terminal (ce typeTop), ce qui est une manière plus que bancal de faire de l'algèbre. D'autant que de nombreuses catégories de structures algébriques n'ont pas d'objet terminal, à commencer par toutes celles qui ont des opérateurs binaires (addition, multiplication), où il devient alors vraiment ridicule de suivre le paradigme du tout objet.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.