Il y a un article classique intitulé Sur les critères à utiliser dans la décomposition des systèmes en modules que je viens de lire pour la première fois. Cela est parfaitement logique pour moi et est probablement l'un de ces articles sur lesquels la POO était basée. Sa conclusion:
Nous avons essayé de démontrer par ces exemples qu'il est presque toujours incorrect de commencer la décomposition d'un système en modules sur la base d'un organigramme. ... Chaque module est alors conçu pour cacher une telle décision aux autres
À mon avis, sans instruction et sans expérience, la programmation fonctionnelle suit exactement le contraire de cet article. Ma compréhension est que la programmation fonctionnelle rend le flux de données idiomatique. Les données sont transmises de fonction en fonction, chaque fonction étant intimement consciente des données et les "modifiant" en cours de route. Et je pense avoir vu un discours de Rich Hickey où il parle de la façon dont le masquage des données est surfait ou inutile ou quelque chose, mais je ne m'en souviens pas avec certitude.
- Je veux d'abord savoir si mon évaluation est correcte. Le paradigme de la PF et cet article sont-ils philosophiquement en désaccord?
- En supposant qu'ils ne sont pas d'accord, comment FP "compense" -il le manque de données cachées? Peut-être sacrifient-ils le masquage des données mais gagnent-ils X, Y et Z. J'aimerais savoir pourquoi X, Y et Z sont considérés comme plus avantageux que le masquage des données.
- Ou, en supposant qu'ils ne sont pas d'accord, FP pense peut-être que le masquage des données est mauvais. Si oui, pourquoi pense-t-il que le masquage des données est mauvais?
- En supposant qu'ils soient d'accord, j'aimerais savoir ce qu'est la mise en œuvre par les FP de la dissimulation des données. Il est évident de voir cela dans OOP. Vous pouvez avoir un
private
champ auquel personne en dehors de la classe ne peut accéder. Il n'y a pas d'analogie évidente avec cela dans FP. - Je pense qu'il y a d'autres questions que je devrais poser, mais je ne sais pas que je devrais poser. N'hésitez pas à y répondre également.
Mise à jour
J'ai trouvé cet exposé de Neal Ford qui contient une diapositive très pertinente. Je vais intégrer la capture d'écran ici:
Réponses:
L'article que vous mentionnez concerne la modularité en général et s'appliquerait également aux programmes structurés, fonctionnels et orientés objet. J'ai déjà entendu parler de cet article par quelqu'un qui était un grand gars de la POO, mais je l'ai lu comme un article sur la programmation en général, pas quelque chose de spécifique à la POO. Il y a un article célèbre sur la programmation fonctionnelle, Why Functional Programming Matters , et la première phrase de la conclusion déclare "Dans cet article, nous avons soutenu que la modularité est la clé d'une programmation réussie." La réponse à (1) est donc non.
Les fonctions bien conçues n'assument pas plus sur leurs données que nécessaire, donc la partie sur "intimement consciente des données" est fausse. (Ou au moins aussi faux que cela pourrait être de la POO. Vous ne pouvez pas programmer strictement à un niveau d'abstraction élevé et ignorer tous les détails pour toujours dans n'importe quel paradigme. En fin de compte, une partie du programme doit réellement connaître la détails spécifiques des données.)
Le masquage des données est un terme spécifique à la POO, et il n'est pas exactement le même que le masquage des informations discuté dans l'article. Les informations qui se cachent dans l'article concernent des décisions de conception difficiles à prendre ou susceptibles de changer. Toutes les décisions de conception concernant un format de données ne sont pas difficiles ou susceptibles de changer, et toutes les décisions difficiles ou susceptibles de changer ne concernent pas un format de données. Personnellement, je ne vois pas pourquoi les programmeurs OO veulent que tout soit un objet. Parfois, une simple structure de données suffit.
Edit: J'ai trouvé une citation pertinente d' une interview avec Rich Hickey .
la source
Pas vraiment, mais cela a ajouté à la discussion, spécialement aux praticiens qui, à l'époque, étaient formés pour décomposer les systèmes en utilisant les premiers critères qu'il décrit dans l'article.
De plus, à mes yeux, votre description de ce à quoi ressemble un programme de PF n'est pas différente de toute autre qui utilise des procédures ou des fonctions:
... sauf pour la partie "intimité", puisque vous pouvez (et avez souvent) des fonctions fonctionnant sur des données abstraites, précisément pour éviter l'intimité. Ainsi, vous avez un certain contrôle sur cette «intimité» et vous pouvez la réguler comme bon vous semble, en configurant des interfaces (c'est-à-dire des fonctions) pour ce que vous voulez cacher.
Donc, je ne vois aucune raison pour laquelle nous ne serions pas en mesure de suivre les critères Parnas de dissimulation d'informations à l'aide de la programmation fonctionnelle et de nous retrouver avec une implémentation d'un index KWIC avec des avantages pointus similaires à sa deuxième implémentation.
En ce qui concerne les données, vous pouvez élaborer des abstractions de données et des abstractions de types de données à l'aide de FP. N'importe laquelle de ces structures cache des structures en béton et des manipulations de ces structures en béton en utilisant des fonctions comme abstractions.
MODIFIER
Il y a un nombre croissant d'affirmations ici affirmant que «cacher des données» dans le contexte de la PF n'est pas si utile (ou OOP-ish (?)). Alors, laissez-moi tamponner ici un exemple très simple et clair du SICP:
Supposons que votre système doive fonctionner avec des nombres rationnels. Vous pouvez éventuellement les représenter sous la forme d'une paire ou d'une liste de deux nombres entiers: le numérateur et le dénominateur. Ainsi:
Si vous ignorez l'abstraction des données, vous obtiendrez très probablement le numérateur et le dénominateur en utilisant
car
etcdr
:En suivant cette approche, toutes les parties du système qui manipulent des nombres rationnels sauront qu'un nombre rationnel est un
cons
- ilscons
numéroteront pour créer des rationnels et les extraire en utilisant des opérateurs de liste.Un problème auquel vous pouvez être confronté est lorsque vous devez avoir une forme réduite de nombres rationnels - des changements seront nécessaires dans l'ensemble du système. De plus, si vous décidez de réduire au moment de la création, vous constaterez peut-être plus tard que la réduction lors de l'accès à l'un des termes rationnels est meilleure, ce qui entraînera un autre changement à grande échelle.
Un autre problème est si, hypothétiquement, une représentation alternative pour eux est préférée et que vous décidez d'abandonner la
cons
représentation - changement à grande échelle à nouveau.Tout effort raisonnable pour faire face à ces situations commencera probablement à cacher la représentation des logiques derrière les interfaces. À la fin, vous pourriez vous retrouver avec quelque chose comme ceci:
(make-rat <n> <d>)
renvoie le nombre rationnel dont le numérateur est l'entier<n>
et dont le dénominateur est l'entier<d>
.(numer <x>)
renvoie le numérateur du nombre rationnel<x>
.(denom <x>)
renvoie le dénominateur du nombre rationnel<x>
.et le système ne saura plus (et ne devrait plus) savoir de quoi sont faites les justifications. C'est parce que
cons
,car
etcdr
ne sont pas intrinsèques aux rationnels, maismake-rat
,numer
et ledenom
sont . Bien sûr, cela pourrait facilement être un système de PF. Ainsi, la "dissimulation de données" (dans ce cas, mieux connue sous le nom d'abstraction de données, ou l'effort d'encapsuler des représentations et des structures concrètes) se présente comme un concept pertinent et une technique largement utilisée et explorée, que ce soit dans le contexte de l'OO, de la programmation fonctionnelle ou peu importe.Et le fait est ... bien que l'on puisse essayer de faire des distinctions entre le "type de dissimulation" ou l'encapsulation qu'ils font (qu'ils cachent une décision de conception, ou des structures de données ou des algorithmes - dans le cas d'abstractions procédurales), tous ont le même thème: ils sont motivés par un ou plusieurs points explicités par Parnas. C'est:
L'exemple ci-dessus a été tiré du livre SICP donc, pour la discussion complète et la présentation de ces concepts dans le livre, je recommande fortement de consulter le chapitre 2 . Je recommande également de se familiariser avec les types de données abstraits dans le contexte de la FP, ce qui apporte d'autres problèmes à la table.
la source
Votre conviction que la programmation fonctionnelle manque de masquage des données est fausse. Il faut simplement une approche différente pour masquer les données. L'un des moyens les plus courants de masquer des données dans la programmation fonctionnelle consiste à utiliser des fonctions polymorphes qui prennent une fonction comme argument. Par exemple, cette fonction
ne peut voir que la structure la plus externe des données (c'est-à-dire qu'il s'agit d'une liste), il ne peut rien voir des données que la liste contient et ne peut opérer sur les données que via la fonction unique qui lui est transmise.
La fonction passée en argument est analogue à une méthode publique sur le type de données que contient la liste. Il fournit un moyen limité d'opérer sur les données, mais n'expose pas le fonctionnement interne du type de données.
la source
Je vais me lancer sur une branche ici et dire que le concept n'est tout simplement pas pertinent en PF comme il l'est en OO.
tl; dr; Le but de la dissimulation des données est de s'assurer que les responsabilités sont maintenues là où elles devraient être, et que vous n'avez pas d'acteurs extérieurs jouant avec des données pour lesquelles ils n'ont pas les connaissances. Dans FP, les données sont générées par des expressions, et de cette façon, vous ne pouvez pas jouer avec les données car ce ne sont pas autant des propriétés mutables que des calculs composables qui changent complètement les règles du jeu.
Dans mes expériences avec FP; qui sont certes insignifiantes, j'ai tendance à trouver un contraste frappant avec OO dans ce qui dénote une bonne / commune modélisation des données.
Ce contraste est qu'en OO en général, vous modélisez des choses pour représenter vos données. Analogie obligatoire avec la voiture:
OO
La chose à noter ici est que lorsque vous modélisez des choses dans un format OO, il s'agit de représenter des choses en tant que données. Vous avez des objets avec des propriétés, beaucoup de ces propriétés sont des objets avec plus de propriétés. Vous avez ici et là quelques méthodes attachées à ces objets, mais tout ce qu'elles font est généralement de modifier les propriétés des objets de cette façon et cela, encore une fois, c'est une modélisation très centrée sur les données; c'est-à-dire que vous modélisez vos données pour qu'elles interagissent en vous concentrant sur leur structuration pour rendre disponibles tous les points de vos données afin que les consommateurs puissent modifier les données de cette façon et cela.
FP
La grande différence entre OO et FP qui me frappe constamment est comme je l'ai dit ci-dessus la façon dont vous modélisez les données. En OO, comme mentionné ci-dessus, vous modélisez les données en tant que données, en FP, vous modélisez les données en tant que calculs, expressions, algorithmes, il s'agit plus de modéliser les activités de vos données que les faits. Pensez à la modélisation de données de base en mathématiques, il s'agit toujours d'obtenir une équation qui peut générer vos données, qui modélise vos données comme l'activité qui les provoque, contrairement à OO, la modélisation propose un moyen de représenter les données que vous avez. C'est en grande partie la distinction entre FP et OO.
Rappelez-vous, pendant une longue période, LISP, l'un des langages fondamentaux de FP, a vécu avec une très petite quantité de types de données primitifs. Cela fonctionne parce que l'approche ne consiste pas tant à modéliser des représentations complexes de vos données que des calculs qui génèrent et expriment les comportements de votre système.
Lorsque je commence à écrire du code dans FP, je commence par écrire du code qui fait quelque chose, alors que lorsque je commence à écrire du code en OO, je commence par écrire des modèles qui décrivent quelque chose. Le faire des choses est caché dans FP en étant des expressions, le faire des choses est exposé dans OO en étant décrit avec des données, cacher ces données limite ladite exposition.
Pour en revenir à la question, que dit FP à propos de la dissimulation de données, l'apprécie-t-elle ou est-elle en désaccord ou non?
Je dis que cela n'a pas d'importance, dans OO, vos données sont les tripes et les éléments importants de votre programme qui devraient être cachés. Dans FP, les tripes et les connaissances de votre système sont toutes cachées dans les algorithmes et les calculs qui expriment le système. Celles-ci sont par définition plus ou moins immuables, la seule façon de muter les expressions de calcul sont des choses comme les macros, mais même dans ce cas, les définitions de mutations sont des expressions elles-mêmes qui ne peuvent pas être encore plus manipulées.
la source
Il y a un peu de paradoxe ici. Même si la programmation fonctionnelle se concentre sur les fonctions, et a souvent des fonctions qui fonctionnent directement sur les types de données primitifs, elle a tendance à cacher plus de données que la programmation orientée objet.
Comment en est-il ainsi? Pensez à une belle interface OO qui cache les données sous-jacentes - peut-être des collections (j'essaie de choisir quelque chose de presque omniprésent). Vous n'aurez peut-être pas besoin de connaître le type sous-jacent des objets de la collection ou le type d'objet implémentant la collection, tant que vous savez que la collection implémente, par exemple, IEnumerable. Vous avez donc des données qui se cachent.
Dans la programmation fonctionnelle, vous pouvez écrire une fonction qui fonctionne efficacement avec une interface IEnumerable, mais qui fonctionne sur un type de données primitif (ou sur n'importe quel type de données). Mais que faire si le type n'a jamais implémenté les méthodes IEnumerable? Voici la clé, vous pouvez toujours avoir les "méthodes" qui forment les pièces nécessaires de "l'interface" être des paramètres transmis à votre fonction. Ou vous pouvez assembler des fonctions avec des données et faire des choses comme une OO.
Notez que dans les deux cas, vous ne cachez pas moins de données que dans OO. Ma fonction générale qui fonctionne sur n'importe quel type n'accède clairement pas aux données de ce type - cela se produit dans les fonctions passées en tant que paramètres à la fonction générale, mais la fonction générale ne jette jamais un œil à l'intérieur de ces fonctions pour voir les données.
Donc, en ce qui concerne votre point 1, je ne pense pas que FP et l'article soient vraiment en désaccord. Je ne pense pas que votre caractérisation de FP ne cachant pas de données soit correcte. On pourrait certainement mettre en œuvre le design que l'auteur a préféré dans FP.
En ce qui concerne le point 4 (2 et 3 n'ont pas de sens pour répondre étant donné ce que j'ai dit pour le point 1), cela varie. Il varie également dans les langues OO et, dans de nombreux domaines privés, il est privé par convention plutôt que appliqué par la langue.
la source
Tout d'abord, merci pour le lien vers cet excellent article, je ne le savais pas jusqu'à présent, et cela m'a donné une grande contribution sur certaines choses dont je discutais avec d'autres concepteurs de logiciels de la communauté ces dernières années. Voici mon avis à ce sujet:
La conception de FP se concentre beaucoup sur le flux de données (ce qui est à mon humble avis pas aussi mauvais que l'article peut le laisser entendre). S'il s'agit d'un "désaccord" complet, on peut faire valoir.
À mon humble avis, il ne compense pas. Voir ci-dessous.
Je ne pense pas que la plupart des utilisateurs ou des concepteurs de FP ressentent ou pensent de cette façon, voir ci-dessous.
Voici le point - vous avez probablement vu tant de systèmes OOP implémentés de manière non fonctionnelle que vous pensez que l'OOP n'est pas fonctionnel. Et c'est une erreur, IMHO OOP et FP sont principalement des concepts orthogonaux, et vous pouvez parfaitement construire des systèmes OO fonctionnels, ce qui vous donne une réponse évidente à votre question. L'implémentation classique "objet" dans FP se fait en utilisant des fermetures , et si vous voulez que les objets soient utilisés dans un système fonctionnel, le point clé est de les concevoir immuables.
Donc, pour créer des systèmes plus grands, à mon humble avis, vous pouvez créer des modules, des classes et des objets en utilisant les concepts OO, exactement de la manière décrite sous "Modularisation 2" dans l'article sans quitter le "chemin FP". Vous utiliserez le concept de module de votre langage FP préféré, rendrez tous vos objets immuables et utiliserez le "meilleur des deux mondes".
la source
TL; DR : Non
Le paradigme de la PF et cet article sont-ils philosophiquement en désaccord?.
Non, ce n'est pas le cas. La programmation fonctionnelle est déclarative qui est «un style de construction de la structure et des éléments des programmes informatiques, qui exprime la logique d'un calcul sans décrire son flux de contrôle». Il s'agit moins de suivre l'organigramme et plus de créer des règles qui permettent au flux de se produire de lui-même.
La programmation procédurale est beaucoup plus proche d'un encodage d'un organigramme que la programmation fonctionnelle. Il s'ensuit que les transformations qui se produisent et codent ces transformations en procédures qui sont exécutées dans l'ordre, exactement comme le flux décrit dans un organigramme.
Masquage des données
la source