Je me suis intéressé récemment à certains des concepts de la programmation fonctionnelle. J'utilise OOP depuis un certain temps maintenant. Je peux voir comment je créerais une application assez complexe dans la POO. Chaque objet saurait comment faire les choses que fait cet objet. Ou tout ce que la classe des parents fait aussi. Je peux donc simplement dire Person().speak()
de faire parler la personne.
Mais comment faire des choses similaires dans la programmation fonctionnelle? Je vois comment les fonctions sont des objets de première classe. Mais cette fonction ne fait qu'une chose spécifique. Aurais-je simplement une say()
méthode flottante et je l'appellerais avec un équivalent d' Person()
argument pour que je sache quel genre de chose dit quelque chose?
Je peux donc voir les choses simples, comment pourrais-je faire la comparaison de POO et d'objets dans la programmation fonctionnelle, afin de pouvoir modulariser et organiser ma base de code?
Pour référence, mon expérience principale avec OOP est Python, PHP et certains C #. Les langages que je regarde qui ont des fonctionnalités sont Scala et Haskell. Bien que je me penche vers Scala.
Exemple de base (Python):
Animal(object):
def say(self, what):
print(what)
Dog(Animal):
def say(self, what):
super().say('dog barks: {0}'.format(what))
Cat(Animal):
def say(self, what):
super().say('cat meows: {0}'.format(what))
dog = Dog()
cat = Cat()
dog.say('ruff')
cat.say('purr')
Réponses:
Ce que vous demandez vraiment ici, c'est comment faire du polymorphisme dans les langages fonctionnels, c'est-à-dire comment créer des fonctions qui se comportent différemment en fonction de leurs arguments.
Notez que le premier argument d'une fonction est généralement équivalent à l '"objet" dans la POO, mais dans les langages fonctionnels, vous voulez généralement séparer les fonctions des données, donc "l'objet" est probablement une valeur de données pure (immuable).
Les langages fonctionnels offrent en général diverses options pour réaliser le polymorphisme:
À titre d'exemple, voici une implémentation Clojure de votre problème en utilisant plusieurs méthodes:
Notez que ce comportement ne nécessite pas de classes explicites à définir: les cartes régulières fonctionnent bien. La fonction de répartition (: tapez dans ce cas) peut être n'importe quelle fonction arbitraire des arguments.
la source
Ce n'est pas une réponse directe, ni nécessairement exacte à 100% car je ne suis pas un expert en langage fonctionnel. Mais dans les deux cas, je vais partager avec vous mon expérience ...
Il y a environ un an, j'étais dans un bateau similaire à vous. J'ai fait du C ++ et du C # et toutes mes conceptions étaient toujours très lourdes pour la POO. J'ai entendu parler des langages FP, lu quelques informations en ligne, feuilleté le livre F # mais je n'arrivais toujours pas à comprendre comment un langage FP peut remplacer la POO ou être utile en général car la plupart des exemples que j'ai vus étaient tout simplement trop simples.
Pour moi, la "percée" est survenue lorsque j'ai décidé d'apprendre le python. J'ai téléchargé python, puis je suis allé sur la page d'accueil du projet euler et j'ai juste commencé à faire un problème après l'autre. Python n'est pas nécessairement un langage FP et vous pouvez certainement y créer des classes, mais par rapport à C ++ / Java / C #, il a beaucoup plus de constructions FP, donc quand j'ai commencé à jouer avec, j'ai pris une décision consciente de ne pas définir une classe, sauf si je devais absolument le faire.
Ce que j'ai trouvé intéressant à propos de Python, c'est à quel point il était facile et naturel de prendre des fonctions et de les "assembler" pour créer des fonctions plus complexes et à la fin votre problème était toujours résolu en appelant une seule fonction.
Vous avez souligné que lors du codage, vous devez suivre le principe de la responsabilité unique et c'est tout à fait correct. Mais ce n'est pas parce que la fonction est responsable d'une seule tâche qu'elle ne peut faire que le strict minimum. Dans FP, vous avez toujours des niveaux d'abstraction. Ainsi, vos fonctions de niveau supérieur peuvent toujours faire «une» chose, mais elles peuvent déléguer à des fonctions de niveau inférieur pour implémenter des détails plus fins sur la façon dont cette «une» chose est réalisée.
Cependant, la clé avec FP est que vous n'avez pas d'effets secondaires. Tant que vous traitez l'application comme une simple transformation de données avec un ensemble défini d'entrées et un ensemble de sorties, vous pouvez écrire du code FP qui accomplirait ce dont vous avez besoin. De toute évidence, toutes les applications ne s'intégreront pas bien dans ce moule, mais une fois que vous aurez commencé à le faire, vous serez surpris du nombre d'applications qui conviennent. Et c'est là que je pense que Python, F # ou Scala brillent parce qu'ils vous donnent des constructions FP mais quand vous devez vous souvenir de votre état et "introduire des effets secondaires", vous pouvez toujours vous rabattre sur des techniques de POO vraies et éprouvées.
Depuis lors, j'ai écrit tout un tas de code python en tant qu'utilitaires et autres scripts d'aide pour le travail interne et certains d'entre eux ont évolué assez loin, mais en se souvenant des principes de base de SOLID, la plupart de ce code est toujours sorti très maintenable et flexible. Tout comme dans OOP, votre interface est une classe et vous déplacez les classes lorsque vous refactorisez et / ou ajoutez des fonctionnalités, dans FP, vous faites exactement la même chose avec les fonctions.
La semaine dernière, j'ai commencé à coder en Java et depuis lors, presque quotidiennement, on me rappelle que lorsque dans la POO, je dois implémenter des interfaces en déclarant des classes avec des méthodes qui remplacent les fonctions, dans certains cas, je pourrais réaliser la même chose en Python en utilisant un une simple expression lambda, par exemple, 20-30 lignes de code que j'ai écrites pour scanner un répertoire, aurait été 1-2 lignes en Python et aucune classe.
Les FP eux-mêmes sont des langues de niveau supérieur. En Python (désolé, ma seule expérience FP), je pouvais rassembler la compréhension de liste dans une autre compréhension de liste avec des lambdas et d'autres trucs ajoutés et le tout ne serait que 3-4 lignes de code. En C ++, je pouvais absolument accomplir la même chose, mais parce que C ++ est de niveau inférieur, je devrais écrire beaucoup plus de code que 3-4 lignes et à mesure que le nombre de lignes augmente, ma formation SRP se déclencherait et je commencerais réfléchir à la façon de diviser le code en morceaux plus petits (c.-à-d. plus de fonctions). Mais dans l'intérêt de la maintenabilité et de masquer les détails de mise en œuvre, je voudrais mettre toutes ces fonctions dans la même classe et les rendre privées. Et voilà ... je viens de créer une classe alors qu'en python j'aurais écrit "return (.... lambda x: .. ....)"
la source
À Haskell, le plus proche est "classe". Cette classe, bien que différente de la classe en Java et C ++ , fonctionnera pour ce que vous voulez dans ce cas.
Dans votre cas, voici à quoi ressemblera votre code.
Ensuite, vous pouvez avoir des types de données individuels adaptant ces méthodes.
EDIT: - Avant de vous spécialiser, dites Dog, vous devez indiquer au système que Dog est un animal.
EDIT: - Pour Wilq.
Maintenant, si vous voulez utiliser say dans une fonction say foo, vous devrez dire à haskell que foo ne peut fonctionner qu'avec Animal.
maintenant, si vous appelez foo avec un chien, il aboie, si vous appelez avec un chat, il miaule.
Vous ne pouvez plus avoir aucune autre définition de fonction. Si say est appelé avec quelque chose qui n'est pas animal, cela provoquera une erreur de compilation.
la source
Les langages fonctionnels utilisent 2 constructions pour réaliser le polymorphisme:
La création de code polymorphe avec ceux-ci est complètement différente de la façon dont la POO utilise l'héritage et les méthodes virtuelles. Alors que les deux peuvent être disponibles dans votre langage OOP préféré (comme C #), la plupart des langages fonctionnels (comme Haskell) font grimper jusqu'à onze. Il est rare de fonctionner pour être non générique et la plupart des fonctions ont des fonctions comme paramètres.
Il est difficile d'expliquer comme ça et il vous faudra beaucoup de temps pour apprendre cette nouvelle façon. Mais pour ce faire, vous devez complètement oublier la POO, car ce n'est pas ainsi que cela fonctionne dans le monde fonctionnel.
la source
cela dépend vraiment de ce que vous voulez accomplir.
si vous avez juste besoin d'un moyen d'organiser le comportement en fonction de critères sélectifs, vous pouvez utiliser par exemple un dictionnaire (table de hachage) avec des objets fonction. en python, cela pourrait être quelque chose comme:
notez cependant que (a) il n'y a pas d '«instances» de chien ou de chat et (b) vous devrez suivre vous-même le «type» de vos objets.
comme par exemple:
pets = [['martin','dog','grrrh'], ['martha', 'cat', 'zzzz']]
. alors vous pourriez faire une compréhension de liste comme[animals[pet[1]]['say'](pet[2]) for pet in pets]
la source
Les langages OO peuvent être utilisés à la place des langages de bas niveau pour parfois s'interfacer directement avec une machine. C ++ Bien sûr, mais même pour C #, il existe des adaptateurs et autres. Bien qu'il soit préférable d'écrire du code pour contrôler les pièces mécaniques et d'avoir un contrôle minutieux de la mémoire aussi près que possible du niveau le plus bas. Mais si cette question est liée aux logiciels orientés objet actuels tels que Line Of Business, les applications Web, IOT, les services Web et la majorité des applications de masse, alors ...
Réponse, le cas échéant
Les lecteurs peuvent essayer de travailler avec une architecture orientée services (SOA). Autrement dit, DDD, N-Layered, N-Tiered, Hexagonal, que ce soit. Je n'ai pas vu une application de grande entreprise utiliser efficacement les OO (Active-Record ou Rich-Models) "traditionnels" comme cela a été décrit dans les années 70 et 80 au cours de la dernière décennie +. (Voir note 1)
La faute n'est pas à l'OP, mais il y a quelques problèmes avec la question.
L'exemple que vous fournissez est simplement de démontrer le polymorphisme, ce n'est pas du code de production. Parfois, des exemples exactement comme ça sont pris au pied de la lettre.
Dans FP et SOA, les données sont séparées de la logique métier. Autrement dit, les données et la logique ne vont pas ensemble. La logique entre dans les services et les données (modèles de domaine) n'ont pas de comportement polymorphe (voir la note 2).
Les services et fonctions peuvent être polymorphes. Dans FP, vous passez fréquemment des fonctions en tant que paramètres à d'autres fonctions au lieu de valeurs. Vous pouvez faire la même chose dans les langages OO avec des types comme Callable ou Func, mais cela ne fonctionne pas de manière rampante (voir la note 3). Dans FP et SOA, vos modèles ne sont pas polymorphes, seulement vos services / fonctions. (Voir note 4)
Il y a un mauvais cas de codage en dur dans cet exemple. Je ne parle pas seulement de la chaîne de couleur rouge "chien aboie". Je parle également du CatModel et du DogModel eux-mêmes. Que se passe-t-il lorsque vous souhaitez ajouter un mouton? Vous devez entrer dans votre code et créer un nouveau code? Pourquoi? Dans le code de production, je préfère voir juste un AnimalModel avec ses propriétés. Au pire, un AmphibianModel et un FowlModel si leurs propriétés et leur manipulation sont si différentes.
Voici ce que j'attends de voir dans un langage "OO" actuel:
Comment passez-vous des classes en OO à la programmation fonctionnelle? Comme d'autres l'ont dit; Vous pouvez, mais pas vraiment. Le but de ce qui précède est de démontrer que vous ne devriez même pas utiliser de classes (dans le sens traditionnel du monde) lorsque vous faites Java et C #. Une fois que vous aurez commencé à écrire du code dans une architecture orientée services (DDD, en couches, hiérarchisée, hexagonale, peu importe), vous serez un peu plus près du fonctionnel car vous séparez vos données (modèles de domaine) de vos fonctions logiques (services).
OO Language un pas de plus vers la PF
Vous pouvez même aller un peu plus loin et diviser vos services SOA en deux types.
Facultatif Type de classe 1 : Services communs de mise en œuvre d'interface pour les points d'entrée. Il s'agit de points d'entrée "impurs" qui peuvent faire appel à d'autres fonctionnalités "pures" ou "impures". Cela peut être vos points d'entrée à partir d'une API RESTful.
Facultatif Type de classe 2 : Pure Business Logic Services. Ce sont des classes statiques qui ont une fonctionnalité "pure". Dans FP, "Pure" signifie qu'il n'y a pas d'effets secondaires. Il ne définit explicitement l'état ou la persistance nulle part. (Voir note 5)
Donc, lorsque vous pensez aux classes dans les langages orientés objet, utilisées dans une architecture orientée services, cela profite non seulement à votre code OO, mais il commence à faire en sorte que la programmation fonctionnelle semble très facile à comprendre.
Remarques
Remarque 1 : la conception orientée objet "riche" ou "enregistrement actif" est toujours présente. Il y a BEAUCOUP de code hérité comme celui-là à l'époque où les gens le faisaient correctement il y a une décennie ou plus. La dernière fois que j'ai vu ce type de code (fait correctement), il provenait d'un jeu vidéo Codebase en C ++ où ils contrôlaient précisément la mémoire et avaient un espace très limité. Cela ne veut pas dire que FP et les architectures orientées services sont des bêtes et ne devraient pas considérer le matériel. Mais ils placent la capacité de changer constamment, d'être maintenue, d'avoir des tailles de données variables et d'autres aspects comme priorité. Dans les jeux vidéo et l'IA machine, vous contrôlez très précisément les signaux et les données.
Remarque 2 : les modèles de domaine n'ont pas de comportement polymorphe ni de dépendances externes. Ils sont "isolés". Cela ne signifie pas qu'ils doivent être 100% anémiques. Ils peuvent avoir beaucoup de logique liée à leur construction et à la modification des propriétés mutables, le cas échéant. Voir DDD "Value Objects" et Entités par Eric Evans et Mark Seemann.
Remarque 3 : Linq et Lambda sont très courants. Mais lorsqu'un utilisateur crée une nouvelle fonction, il utilise rarement Func ou Callable comme paramètres, alors que dans FP, il serait bizarre de voir une application sans fonctions suivant ce modèle.
Note 4 : Ne confondez pas le polymorphisme avec l'héritage. Un CatModel peut hériter d'AnimalBase pour déterminer les propriétés d'un animal. Mais comme je le montre, des modèles comme celui-ci sont une odeur de code . Si vous voyez ce modèle, vous pourriez envisager de le décomposer et de le transformer en données.
Remarque 5 : Les fonctions pures peuvent (et acceptent) des fonctions en tant que paramètres. La fonction entrante peut être impure, mais peut être pure. À des fins de test, ce serait toujours pur. Mais en production, bien qu'il soit traité comme pur, il peut contenir des effets secondaires. Cela ne change pas le fait que la fonction pure est pure. Bien que la fonction de paramètre puisse être impure. Pas déroutant! :RÉ
la source
Vous pourriez faire quelque chose comme ça .. php
la source
$whostosay
devient le type d'objet qui détermine ce qui est exécuté. Ce qui précède peut être modifié pour accepter en plus un autre paramètre$whattosay
afin qu'un type qui le prend en charge (par exemple'human'
) puisse en faire usage.