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?
functional-programming
Andrew Martin
la source
la source
Réponses:
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
Set
en entrée une particule et renvoie une nouvelleSet
particule 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.la source
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.
la source
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.
la source
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.
la source