Gérer les problèmes d'état dans la programmation fonctionnelle


18

J'ai appris à programmer principalement du point de vue de la POO (comme la plupart d'entre nous, j'en suis sûr), mais j'ai passé beaucoup de temps à essayer d'apprendre à résoudre les problèmes de manière fonctionnelle. J'ai une bonne compréhension de la façon de résoudre les problèmes de calcul avec FP, mais quand il s'agit de problèmes plus compliqués, je me retrouve toujours à avoir besoin d'objets mutables. Par exemple, si j'écris un simulateur de particules, je veux que les "objets" de particules avec une position modifiable soient mis à jour. Comment les problèmes intrinsèquement «avec état» sont-ils généralement résolus en utilisant des techniques de programmation fonctionnelle?


4
La première étape consiste probablement à réaliser que les problèmes ne sont pas intrinsèquement statiques.
Telastyn

4
Certains problèmes sont intrinsèquement complexes, comme l'écriture dans une base de données ou le dessin d'une interface graphique. En prenant mon exemple de simulateur de particules, quelle serait une autre façon d'y penser? Renvoyer de nouvelles particules à chaque mise à jour de leur position afin d'éviter un état me semble inefficace, et pas un bon modèle du monde réel.
Andrew Martin

4
Sauf peut-être pour l'exemple de base de données, ces problèmes ne sont pas intrinsèquement avec état. Par exemple, pour la programmation GUI, vous utilisez vraiment l'état mutable comme un modèle de temps implicite pauvre ; la programmation réactive fonctionnelle vous permet de modéliser le temps de manière explicite sans dépendre de l'état en fournissant des flux d'événements que vous pouvez combiner.
Tikhon Jelvis

1
Il existe une solution plus simple: lorsque vous rencontrez un problème qui n'est pas facilement modélisé avec des techniques de FP, n'utilisez pas de programmation fonctionnelle pour le résoudre. Le bon outil pour le travail et tout ça ...
Mason Wheeler

1
@AndrewMartin Pas un bon modèle du monde réel? Les mathématiques utilisées en physique pour modéliser le monde réel sont purement fonctionnelles. Avec un bon ramasse-miettes, allouer un objet est aussi bon marché que cogner un pointeur, et le temps de collecte est proportionnel au nombre d' objets vivants . Si quoi que ce soit, je parierais que la principale source d'inefficacité dans la programmation fonctionnelle est l'utilisation de structures de données qui ne sont pas efficaces en cache. Les listes liées et les arbres binaires ne sont pas exactement des affiches de l'efficacité du cache.
Doval

Réponses:


20

Les programmes fonctionnels gèrent très bien l'état, mais nécessitent une manière différente de le voir. Pour votre exemple de position, une chose à considérer est que votre position soit fonction du temps au lieu d'une valeur fixe . Cela fonctionne bien pour les particules suivant un chemin mathématique fixe, mais vous avez besoin d'une stratégie différente pour gérer un changement de chemin, comme après une collision.

La stratégie de base ici est de créer des fonctions qui prennent un état et retournent le nouvel état . Un simulateur de particules serait donc une fonction qui prend Seten entrée une particule et renvoie une nouvelle Setparticule après un pas de temps. Ensuite, vous appelez simplement à plusieurs reprises cette fonction avec son entrée définie sur son résultat précédent.


5
+1 C'est correct d'avoir un état dans FP, mais pas un état mutable .
jhewlett

1
Merci pour cet aperçu. Mes inquiétudes concernant l'inefficacité ont été contrecarrées par @logc; les détails techniques de la transformation de l'état sont un problème d'implémentation de bas niveau que le langage lui-même est censé résoudre. J'ai regardé Rich Hickey expliquer comment il fait ça avec Clojure dans une vidéo.
Andrew Martin

1
@jhewlett: Pour être plus précis: FP a un état, même un état mutable, mais ils ne le représentent pas en utilisant des variables mutables.
Giorgio

9

Comme l'a noté @KarlBielefeldt, l'approche fonctionnelle d'un tel problème consiste à le voir comme renvoyant un nouvel état à partir d'un état précédent. Les fonctions elles-mêmes ne contiennent aucune information, elles mettront donc toujours à jour l'état m à l'état n .

Je pense que vous trouvez cela inefficace parce que vous supposez que l'état précédent doit être conservé en mémoire lors du calcul du nouvel état. Notez que le choix entre écrire un état complètement nouveau ou réécrire l'ancien en place est un détail d'implémentation du point de vue d'un langage fonctionnel.

Par exemple, disons que j'ai une liste d'un million d'entiers et que je veux incrémenter le dixième d'une unité. Copier la liste entière avec un nouveau numéro dans sa dixième position est un gaspillage, vous avez raison; mais ce n'est que la manière conceptuelle de décrire l'opération au compilateur ou à l'interpréteur de langage. Le compilateur ou l'interpréteur est libre de prendre la première liste et d'écraser simplement la dixième position.

L'avantage de décrire l'opération de cette manière est que le compilateur peut raisonner sur la situation lorsque de nombreux threads souhaitent mettre à jour la même liste à différentes positions. Si l'opération est décrite comme "allez à cette position et écrasez ce que vous trouvez", c'est le programmeur, et non le compilateur, qui est chargé de s'assurer que les écrasements n'entrent pas en collision.

Cela dit, même à Haskell, il existe une monade d'État qui aide à modéliser des situations où le «maintien de l'état» est une solution plus intuitive à un problème. Mais veuillez également noter que certains problèmes que vous trouvez " intrinsèquement dynamiques, comme l'écriture dans une base de données " ont des solutions immuables comme Datomic . Cela peut être surprenant jusqu'à ce que vous compreniez que c'est un concept, pas nécessairement sa réalisation.


4
Je pense que l'extrait de mise à jour d'une grande liste est trompeur; Je ne connais aucun compilateur qui effectuera réellement cette optimisation pour vous. Même si le compilateur pouvait le faire, cela n'est possible que dans les cas où vous ne vous en tenez pas aux versions précédentes de la liste. La vraie solution est d'utiliser une structure de données de liste qui ne nécessite pas de copier le tout pour changer un seul élément.
Doval

@Doval: "Même si le compilateur pouvait faire cela, ce n'est possible que dans les cas où vous ne vous accrochez pas aux versions précédentes de la liste.": Cela me rappelle les types uniques de Clean.
Giorgio

4

L'adhésion au bon modèle mental aide à mieux penser et gérer l'état. Dans mon esprit, le meilleur modèle mental est le flip book . Une fois que vous aurez cliqué, vous comprendrez que FP s'appuie fortement sur des structures de données persistantes qui capturent l'état du monde et que des fonctions sont utilisées pour faire la transition de cet état sans aucune mutation.

Rich Hickey éclaire ces idées:

Il y a d'autres discussions mais cela devrait vous envoyer dans la bonne direction.


3

Lors de l'écriture d'applications volumineuses et modérément volumineuses, j'ai souvent trouvé utile de différencier les sections de l'application qui sont avec état et celles qui sont sans état.

Les classes / structures de données de la section avec état stockent les données de l'application et les fonctions de cette section fonctionnent avec une connaissance implicite des données de l'application.

Les classes / structures de données / fonctions de la section sans état sont là pour prendre en charge les aspects purement algorithmiques de l'application. Ils n'ont pas de connaissance implicite des données de l'application. Ils fonctionnent de manière purement fonctionnelle. Les parties avec état de l'application peuvent subir un changement d'état en tant qu'effet secondaire de l'exécution de fonctions dans la section sans état de l'application.

La partie la plus difficile consiste à déterminer quelles classes / fonctions mettre dans la section sans état et quelles classes / fonctions mettre dans la section avec état, et avoir la discipline pour les mettre dans des fichiers / bibliothèques séparés.


Comment cela répond-il à la question? (non-downvoting)
kravemir

@kravemir, que vous écriviez une application en utilisant la POO ou la FP, vous devez comprendre où se trouve l'état de l'application.
R Sahu
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.