Comment puis-je construire un système qui présente toutes les caractéristiques suivantes :
- Utilisation de fonctions pures avec des objets immuables.
- Ne passez dans une fonction que les données dont elle a besoin, pas plus (c'est-à-dire pas de gros objet d'état d'application)
- Évitez d'avoir trop d'arguments pour les fonctions.
- Évitez d'avoir à construire de nouveaux objets uniquement dans le but de compresser et de décompresser les paramètres des fonctions, simplement pour éviter que trop de paramètres ne soient transmis aux fonctions. Si je vais regrouper plusieurs éléments dans une fonction comme un seul objet, je veux que cet objet soit le propriétaire de ces données, pas quelque chose construit temporairement
Il me semble que la monade d'État enfreint la règle n ° 2, bien que ce ne soit pas évident car elle est tissée à travers la monade.
J'ai le sentiment que je dois utiliser les lentilles d'une manière ou d'une autre, mais très peu est écrit à ce sujet pour les langages non fonctionnels.
Contexte
En tant qu'exercice, je convertis une de mes applications existantes d'un style orienté objet en un style fonctionnel. La première chose que j'essaie de faire est de tirer le maximum du noyau interne de l'application.
Une chose que j'ai entendue, c'est que comment gérer "State" dans un langage purement fonctionnel, et c'est ce que je crois que c'est fait par les monades d'État, c'est que logiquement, vous appelez une fonction pure, "en passant dans l'état de la monde tel qu'il est ", puis lorsque la fonction revient, elle vous renvoie l'état du monde tel qu'il a changé.
Pour illustrer, la façon dont vous pouvez faire un "monde bonjour" d'une manière purement fonctionnelle est un peu comme, vous passez dans votre programme cet état de l'écran, et recevez l'état de l'écran avec "bonjour le monde" imprimé dessus. Donc, techniquement, vous appelez une fonction pure et il n'y a pas d'effets secondaires.
Sur cette base, j'ai parcouru mon application et: 1. D'abord, j'ai mis tout mon état d'application dans un seul objet global (GameState) 2. Ensuite, j'ai rendu GameState immuable. Tu ne peux pas le changer. Si vous avez besoin d'un changement, vous devez en construire un nouveau. J'ai fait cela en ajoutant un constructeur de copie, qui prend éventuellement un ou plusieurs champs qui ont changé. 3. À chaque application, je passe le GameState en paramètre. Dans la fonction, une fois qu'il a fait ce qu'il va faire, il crée un nouveau GameState et le renvoie.
Comment j'ai un noyau fonctionnel pur et une boucle à l'extérieur qui alimente ce GameState dans la boucle de flux de travail principale de l'application.
Ma question:
Maintenant, mon problème est que le GameState a environ 15 objets immuables différents. La plupart des fonctions au niveau le plus bas ne fonctionnent que sur quelques-uns de ces objets, tels que le maintien du score. Donc, disons que j'ai une fonction qui calcule le score. Aujourd'hui, le GameState est passé à cette fonction, qui modifie le score en créant un nouveau GameState avec un nouveau score.
Quelque chose à ce sujet semble faux. La fonction n'a pas besoin de l'intégralité de GameState. Il a juste besoin de l'objet Score. Je l'ai donc mis à jour pour transmettre la partition et renvoyer uniquement la partition.
Cela semblait logique, alors je suis allé plus loin avec d'autres fonctions. Certaines fonctions nécessiteraient que je transmette 2, 3 ou 4 paramètres à partir du GameState, mais comme j'ai utilisé le modèle tout au long du noyau externe de l'application, je passe de plus en plus dans l'état de l'application. Par exemple, en haut de la boucle de workflow, j'appellerais une méthode, qui appellerait une méthode qui appellerait une méthode, etc., jusqu'à l'endroit où le score est calculé. Cela signifie que le score actuel est transmis à travers toutes ces couches simplement parce qu'une fonction tout en bas va calculer le score.
Alors maintenant, j'ai des fonctions avec parfois des dizaines de paramètres. Je pourrais mettre ces paramètres dans un objet pour réduire le nombre de paramètres, mais je voudrais que cette classe soit l'emplacement principal de l'état de l'application d'état, plutôt qu'un objet qui est simplement construit au moment de l'appel simplement pour éviter de passer dans plusieurs paramètres, puis décompressez-les.
Alors maintenant, je me demande si le problème que j'ai est que mes fonctions sont imbriquées trop profondément. C'est le résultat de vouloir avoir de petites fonctions, donc je refactorise quand une fonction devient trop grande et je la divise en plusieurs fonctions plus petites. Mais cela produit une hiérarchie plus profonde, et tout ce qui est transmis aux fonctions internes doit être transmis à la fonction externe même si la fonction externe ne fonctionne pas directement sur ces objets.
Il semblait que le simple passage du GameState évitait ce problème. Mais je reviens au problème d'origine de transmettre plus d'informations à une fonction que la fonction n'en a besoin.