Es muy común encontrar libros, cursos e incluso materias universitarias que presentan la programación concurrente como un paradigma, junto a la programación imperativa, orientada a objetos o funcional.
En mi opinión, esa clasificación es incorrecta.
Un paradigma de programación describe cómo se modela y expresa un programa.
Un paradigma de programación es una filosofía o un estilo para construir software. Define las reglas, el modo de pensar y la estructura para organizar y ejecutar las instrucciones de un programa.
Por ejemplo:
- Imperativo: el programa es una secuencia de instrucciones que modifican el estado.
- Orientado a objetos: el programa se organiza mediante objetos que colaboran entre sí.
- Funcional: el programa se construye a partir de funciones y composición.
- Lógico: el programa se expresa mediante hechos y reglas.
La concurrencia responde a otra pregunta completamente distinta: ¿Cómo se ejecutan varias tareas al mismo tiempo y cómo se coordinan entre ellas?
Es decir, la concurrencia es un modelo de programación (o incluso un modelo de ejecución), no un paradigma en sí mismo.
La prueba es muy sencilla: la concurrencia puede resolverse desde prácticamente cualquier paradigma.
- Java y C# utilizan threads, virtual threads, async/await y actores desde un enfoque imperativo/OO.
- Scala combina programación funcional con Futures, Actors, STM o Cats Effect.
- Go implementa CSP sobre un lenguaje imperativo.
- Erlang implementa actores sobre un lenguaje funcional.
- Existen variantes concurrentes de Prolog basadas en programación lógica.
Si la concurrencia fuera realmente un paradigma, no podría aparecer de forma natural en paradigmas tan diferentes.
Lo que realmente cambia es el modelo de concurrencia elegido:
- Threads
- Actores
- CSP
- STM (Software Transactional Memory)
- Async/Await
- Dataflow
Estos modelos pueden implementarse sobre distintos paradigmas.
Por eso resulta más útil separar tres conceptos:
- Paradigma: Imperativo, OO, Funcional, Lógico
- Modelo de concurrencia: Threads, Actores, CSP, STM, Async/Await
- Modelo de ejecución: Secuencial, Concurrente, Paralelo, Distribuido
Esta clasificación evita muchas confusiones y explica por qué un mismo lenguaje puede ser funcional, usar actores y ejecutarse en forma distribuida.
La concurrencia no reemplaza a un paradigma. Lo complementa.
