Les partisans de la PF ont affirmé que la simultanéité est facile parce que leur paradigme évite l'état mutable. Je ne comprends pas.
Je voulais parler de cette question générale en tant que quelqu'un qui est un néophyte fonctionnel mais qui a été à la hauteur de mes globes oculaires en ce qui concerne les effets secondaires au fil des ans et je voudrais les atténuer, pour toutes sortes de raisons, y compris plus faciles (ou spécifiquement "plus sûres, moins sujette aux erreurs "). Quand je jette un coup d'œil à mes pairs fonctionnels et à ce qu'ils font, l'herbe semble un peu plus verte et sent mieux, du moins à cet égard.
Algorithmes série
Cela dit, à propos de votre exemple spécifique, si votre problème est de nature série et que B ne peut pas être exécuté tant que A n'est pas terminé, conceptuellement, vous ne pouvez pas exécuter A et B en parallèle quoi qu'il arrive. Vous devez trouver un moyen de briser la dépendance à l'ordre comme dans votre réponse en effectuant des mouvements parallèles en utilisant l'ancien état du jeu, ou utiliser une structure de données qui permet à certaines parties d'être modifiées indépendamment pour éliminer la dépendance à l'ordre comme proposé dans les autres réponses , ou quelque chose de ce genre. Mais il y a certainement une part de problèmes de conception conceptuelle comme celui-ci où vous ne pouvez pas nécessairement tout simplement multithreader si facilement parce que les choses sont immuables. Certaines choses vont être de nature sérielle jusqu'à ce que vous trouviez un moyen intelligent de briser la dépendance à l'ordre, si cela est même possible.
Accès simultané plus facile
Cela dit, il y a beaucoup de cas où nous ne parvenons pas à paralléliser les programmes qui impliquent des effets secondaires dans des endroits qui pourraient améliorer considérablement les performances simplement en raison de la possibilité qu'il pourrait ne pas être thread-safe. L'un des cas où l'élimination de l'état mutable (ou plus précisément des effets secondaires externes) aide beaucoup, comme je le vois, c'est qu'il transforme "peut ou non être thread-safe" en "définitivement thread-safe" .
Pour rendre cette déclaration un peu plus concrète, considérez que je vous donne une tâche pour implémenter une fonction de tri en C qui accepte un comparateur et l'utilise pour trier un tableau d'éléments. Il est censé être assez généralisé, mais je vais vous donner une hypothèse simple qu'il sera utilisé contre des entrées d'une telle échelle (des millions d'éléments ou plus) qu'il sera sans aucun doute avantageux de toujours utiliser une implémentation multithread. Pouvez-vous multithread votre fonction de tri?
Le problème est que vous ne pouvez pas parce que les comparateurs que votre fonction de tri appelle peuventprovoquer des effets secondaires, sauf si vous savez comment ils sont mis en œuvre (ou à tout le moins documentés) pour tous les cas possibles, ce qui est impossible sans dégénéraliser la fonction. Un comparateur pourrait faire quelque chose de dégoûtant comme modifier une variable globale à l'intérieur d'une manière non atomique. 99,9999% des comparateurs peuvent ne pas le faire, mais nous ne pouvons toujours pas multithreader cette fonction généralisée simplement en raison de 0,00001% de cas qui pourraient provoquer des effets secondaires. En conséquence, vous devrez peut-être proposer à la fois une fonction de tri monothread et multithread et confier la responsabilité aux programmeurs qui l'utilisent pour décider laquelle utiliser en fonction de la sécurité des threads. Et les gens peuvent toujours utiliser la version à un seul thread et manquer des opportunités de multithread, car ils ne savent pas non plus si le comparateur est compatible avec les threads,
Il y a beaucoup de cerveaux qui peuvent être impliqués dans la rationalisation de la sécurité des threads sans jeter de verrous partout, ce qui peut disparaître si nous avions juste des garanties solides que les fonctions ne causeront pas d'effets secondaires pour l'instant et pour l'avenir. Et il y a la peur: la peur pratique, car quiconque a dû déboguer une condition de course trop souvent hésiterait probablement à multithreader tout ce dont il ne peut pas être sûr à 110% qu'il est thread-safe et le restera. Même pour les plus paranoïaques (dont je suis probablement au moins à la limite), la fonction pure fournit ce sentiment de soulagement et de confiance que nous pouvons l'appeler en parallèle en toute sécurité.
Et c'est l'un des principaux cas où je le vois comme si bénéfique si vous pouvez obtenir une garantie solide que ces fonctions sont thread-safe que vous obtenez avec des langages fonctionnels purs. L'autre est que les langages fonctionnels favorisent souvent la création de fonctions exemptes d'effets secondaires en premier lieu. Par exemple, ils peuvent vous fournir des structures de données persistantes où il est raisonnablement assez efficace de saisir une structure de données massive, puis d'en sortir une toute nouvelle avec seulement une petite partie de celle-ci modifiée sans toucher à l'original. Ceux qui travaillent sans de telles structures de données pourraient vouloir les modifier directement et perdre une certaine sécurité des threads en cours de route.
Effets secondaires
Cela dit, je ne suis pas d'accord avec une partie avec tout le respect que je dois à mes amis fonctionnels (qui, je pense, sont super cool):
[...] parce que leur paradigme évite l'état mutable.
Ce n'est pas nécessairement l'immuabilité qui rend la concurrence si pratique que je le vois. Ce sont des fonctions qui évitent de provoquer des effets secondaires. Si une fonction entre un tableau pour trier, le copie, puis mute la copie pour trier son contenu et génère la copie, elle est toujours aussi sécurisée pour les threads que celle qui fonctionne avec un type de tableau immuable même si vous passez la même entrée tableau à partir de plusieurs threads. Je pense donc qu'il y a toujours une place pour les types mutables dans la création de code très concurrentiel, pour ainsi dire, bien qu'il y ait beaucoup d'avantages supplémentaires aux types immuables, y compris les structures de données persistantes que j'utilise pas tant pour leurs propriétés immuables que pour élimine le coût d'avoir à tout copier en profondeur afin de créer des fonctions sans effets secondaires.
Et il y a souvent des frais généraux pour rendre les fonctions exemptes d'effets secondaires sous forme de mélange et de copie de données supplémentaires, peut-être un niveau supplémentaire d'indirection, et peut-être un GC sur des parties d'une structure de données persistante, mais je regarde un de mes copains qui a une machine à 32 cœurs et je pense que l'échange en vaut probablement la peine si nous pouvons faire plus de choses en toute confiance en parallèle.