Los defensores de FP han afirmado que la concurrencia es fácil porque su paradigma evita el estado mutable. No lo entiendo
Quería hablar sobre esta pregunta general como alguien que es un neófito funcional pero que ha estado a la altura de mis ojos en los efectos secundarios a lo largo de los años y me gustaría mitigarlos, por todo tipo de razones, incluso más fácil (o específicamente "más seguro, menos concurrencia "). Cuando miro a mis compañeros funcionales y lo que están haciendo, la hierba parece un poco más verde y huele mejor, al menos en este sentido.
Algoritmos seriales
Dicho esto, sobre su ejemplo específico, si su problema es de naturaleza serial y B no se puede ejecutar hasta que A esté terminado, conceptualmente no puede ejecutar A y B en paralelo sin importar qué. Debe encontrar una manera de romper la dependencia del orden, como en su respuesta, basándose en hacer movimientos paralelos utilizando el estado del juego anterior, o usar una estructura de datos que permita modificar partes de ella de forma independiente para eliminar la dependencia del orden como se propone en las otras respuestas o algo por el estilo. Pero definitivamente hay una serie de problemas de diseño conceptual como este en los que no se puede simplemente multiprocesar todo tan fácilmente porque las cosas son inmutables. Algunas cosas serán de naturaleza serial hasta que encuentres una forma inteligente de romper la dependencia del orden, si eso es posible.
Simultaneidad más fácil
Dicho esto, hay muchos casos en los que no somos capaces de poner en paralelo los programas que implican efectos secundarios en los lugares que podrían mejorar potencialmente el rendimiento significativamente simplemente debido a la posibilidad de que podría no ser seguro para subprocesos. Uno de los casos en los que la eliminación del estado mutable (o más específicamente, los efectos secundarios externos) ayuda mucho, según veo, es que convierte "puede o no ser seguro para subprocesos" en "definitivamente seguro para subprocesos" .
Para hacer esa afirmación un poco más concreta, considere que le doy una tarea para implementar una función de clasificación en C que acepta un comparador y lo usa para ordenar una matriz de elementos. Está destinado a ser bastante generalizado, pero supondré fácilmente que se utilizará contra entradas de tal escala (millones de elementos o más) que sin duda será beneficioso utilizar siempre una implementación multiproceso. ¿Puedes multiprocesar tu función de clasificación?
El problema es que no puede porque los comparadores a los que llama su función de clasificación puedencausar efectos secundarios a menos que sepa cómo se implementan (o al menos se documentan) para todos los casos posibles, lo cual es imposible sin desgeneralizar la función. Un comparador podría hacer algo desagradable como modificar una variable global dentro de una manera no atómica. Es posible que el 99.9999% de los comparadores no hagan esto, pero aún no podemos multiprocesar esta función generalizada simplemente debido al 0.00001% de los casos que pueden causar efectos secundarios. Como resultado, es posible que deba ofrecer una función de clasificación de subprocesos múltiples y de subprocesos múltiples y transferir la responsabilidad a los programadores que la utilizan para decidir cuál usar en función de la seguridad de los subprocesos. Y las personas aún pueden usar la versión de subproceso único y perder oportunidades de subprocesos múltiples porque también pueden no estar seguros de si el comparador es seguro para subprocesos,
Hay una gran cantidad de capacidad intelectual que puede estar involucrada en la racionalización de la seguridad de las cosas sin tirar cerraduras a todas partes que pueden desaparecer si solo tuviéramos garantías firmes de que las funciones no causarán efectos secundarios por ahora y el futuro. Y hay miedo: miedo práctico, porque cualquiera que haya tenido que depurar una condición de carrera demasiadas veces probablemente dudaría en hacer múltiples subprocesos cualquier cosa de la que no pueda estar 110% seguro de que es seguro para subprocesos y permanecerá como tal. Incluso para los más paranoicos (de los cuales probablemente estoy al menos en el límite), la función pura proporciona esa sensación de alivio y confianza que podemos llamar con seguridad en paralelo.
Y ese es uno de los casos principales donde lo veo tan beneficioso si puede obtener una garantía sólida de que tales funciones son seguras para subprocesos que obtiene con lenguajes funcionales puros. El otro es que los lenguajes funcionales a menudo promueven la creación de funciones libres de efectos secundarios en primer lugar. Por ejemplo, podrían proporcionarle estructuras de datos persistentes donde es razonablemente bastante eficiente ingresar una estructura de datos masiva y luego generar una nueva con solo una pequeña parte de ella modificada del original sin tocar el original. Aquellos que trabajan sin tales estructuras de datos pueden querer modificarlos directamente y perder algo de seguridad de subprocesos en el camino.
Efectos secundarios
Dicho esto, no estoy de acuerdo con una parte con el debido respeto a mis amigos funcionales (que creo que son súper geniales):
[...] porque su paradigma evita el estado mutable.
No es necesariamente la inmutabilidad lo que hace que la concurrencia sea tan práctica como la veo. Son funciones que evitan causar efectos secundarios. Si una función ingresa una matriz para ordenar, la copia y luego muta la copia para ordenar su contenido y genera la copia, sigue siendo tan segura como una que trabaja con algún tipo de matriz inmutable, incluso si está pasando la misma entrada matriz de múltiples hilos. Así que creo que todavía hay un lugar para los tipos mutables en la creación de código muy amigable con la concurrencia, por así decirlo, aunque hay muchos beneficios adicionales para los tipos inmutables, incluidas las estructuras de datos persistentes que utilizo no tanto por sus propiedades inmutables sino para elimine el gasto de tener que copiar todo en profundidad para crear funciones libres de efectos secundarios.
Y a menudo hay gastos generales para hacer que las funciones estén libres de efectos secundarios en forma de barajar y copiar algunos datos adicionales, tal vez un nivel adicional de indirección, y posiblemente algo de GC en partes de una estructura de datos persistente, pero miro a uno de mis amigos que tiene una máquina de 32 núcleos y creo que el intercambio probablemente valga la pena si podemos hacer más cosas con confianza en paralelo.