Question

J’ai besoin d’aide pour comprendre la différence entre ma notion d’état POO actuelle et la façon dont cela se ferait dans un langage fonctionnel comme Haskell ou Clojure.

Pour utiliser un exemple banal, supposons que nous traitons avec des objets / structures de compte bancaire simplifiés. Dans un langage POO, une classe aurait une référence à un BankAccount, qui aurait des variables d'instance pour des choses comme le taux d'intérêt, et des méthodes comme setInterestRate () qui changent l'état de l'objet et ne renvoient généralement rien. Dans Clojure, par exemple, j'aurais une structure de compte bancaire (un hashmap glorifié) et des fonctions spéciales qui prennent un paramètre de compte bancaire et d'autres informations et renvoient une nouvelle structure. Ainsi, au lieu de changer l’état de l’objet original, j’en retourne un nouvel avec les modifications souhaitées.

Alors ... qu'est-ce que je fais avec ça? Écraser toute variable référençant l'ancien compte bancaire? Dans l’affirmative, cela présente-t-il des avantages par rapport à l’approche de la POO qui change d’état? En fin de compte, dans les deux cas, il semble que l’on ait une variable qui référence l’objet avec les modifications nécessaires. Retardé comme je suis, je n’ai qu’une vague idée de ce qui se passe.

J'espère que cela a du sens, merci pour toute aide!

Était-ce utile?

La solution

Dans un style purement fonctionnel, vous n'écraserez jamais aucune variable.

Une analogie serait l’espace-temps en physique. Si vous considérez le monde en 3D, les objets n'ont pas de position fixe - ils se déplacent dans le temps. Pour que les mathématiques aient une incidence sur le monde physique, nous ajoutons donc une dimension temporelle et considérons les valeurs de différentes propriétés à des moments particuliers. Ce faisant, nous avons fait les objets de notre étude en constantes. De même, en programmation, travailler avec des valeurs immuables permet une simplicité conceptuelle. Les objets ayant une identité dans le monde réel peuvent être modélisés comme une séquence de valeurs immuables (les états de l'objet à des temps croissants) plutôt que comme une valeur unique qui change.

Bien sûr, les détails sur la manière d’associer la séquence de valeurs à une " identité d’objet " peut être un peu velu. Haskell a Monads qui vous permet de modéliser l'état. La Programmation réactive fonctionnelle est une tentative plus concrète de modéliser des objets du monde avec des mises à jour fonctionnelles pures. penser est une direction très prometteuse pour la programmation.

Je noterai que Clojure, contrairement à Haskell, n’est pas pur, et vous pouvez mettre à jour les variables comme vous l’avez suggéré. Si vous ne mettez à jour que quelques variables à un niveau élevé, vous profiterez probablement encore de la simplicité conceptuelle de la programmation fonctionnelle.

Autres conseils

Vraisemblablement, dans le monde OO, vous avez une boucle et vous modifiez ces comptes bancaires encore et encore en réponse aux demandes. Supposons que vous ayez tout un portefeuille de comptes et que ceux-ci aient le type Portfolio. Ensuite, en Haskell, vous écririez une fonction pure

updatePortfolio :: Request -> Portfolio -> Portfolio

Et votre boucle principale pourrait lire les demandes de l’entrée standard et maintenir votre portefeuille à jour. (L'exemple n'est pas très utile, sauf si vous pouvez également écrire le portfolio, mais c'est plus simple.)

readRequest :: IO Request  -- an action that, when performed, reads a Request with side effects

main :: Portfolio -> IO ()  -- a completely useless program that updates a Portfolio in response to a stream of Requests

main portfolio = do req <- readRequest
                    main (updatePortfolio req)

et maintenant j'espère que vous voyez ce qui est arrivé à votre état mutable: dans un programme fonctionnel typique, indiquez que les modifications sont transmises en tant que paramètres à une fonction. Lorsque l'état change, vous effectuez un nouvel appel de fonction. L'appel est en position de fin (vous pouvez rechercher le «bon appel de fin») et n'utilise donc pas de ressources supplémentaires. En fait, lorsque le compilateur génère le code assembleur, il génère une boucle et conserve le pointeur sur la portefeuille en constante évolution dans un registre.

C’est un exemple très jouet, mais j’espère que cela vous donnera un peu la saveur d’un langage fonctionnel.

Alors ... qu'est-ce que j'en fais? Écraser toute variable référençant l'ancien compte bancaire?

Oui

Si oui, cela présente-t-il des avantages par rapport à l'approche orientée objet à changement d'état?

Supposons que le calcul de l’action que vous effectuez sur cette structure prenne beaucoup de temps et que quelque chose se passe à mi-chemin et que vous deviez revenir à la structure originale, sinon le calcul a généré une erreur. Avec l'interprétation que vous m'avez donnée de OO (en utilisant une référence, car vous pouvez avoir un langage OO immuable), les données pourraient être corrompues - elles sont inconnues à moins que suffisamment d'informations aient été fournies à partir de l'appel de fonction en échec, et nous suggérons qu'elles échouent. mal. Dans une approche fonctionnelle, vous savez avec certitude que votre structure de données d'origine est correcte - car vous en avez initialement fait une copie.

Étendez ce scénario dans des applications multithreads. Nous pouvons nous assurer que personne d'autre n'utilise la structure de données que nous utilisons puisque nous en avons tous notre propre version.

De plus, nous pouvons économiser de l'espace en utilisant les données de l'autre structure à partir de laquelle nous copions. Un exemple classique est l’ajout d’un élément à l’en-tête d’une liste. Si nous avons un pointeur sur le deuxième élément et un pointeur sur le premier élément, nous pouvons référencer les deux listes avec uniquement la taille de la première (voir ci-dessous). Sans immuabilité, nous ne pouvons pas garantir cela.

        b__
           |  
a -> [6|] -+-> [5|] -> [4|] -> [3|] -> [2|] -> [1|x]

Regardez Haskell, qui est un langage purement fonctionnel & # 8212; il n'a pas de réassignation, ni aucun autre effet secondaire: pour faire des E / S, dans le La monade IO sa construction remplace en réalité le RealWorld avec une nouvelle instance du monde comportant, par exemple, un nouveau texte affiché dans la console.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top