Clojure proporciona un estilo híbrido de programación funcional y estado mutable para resolver problemas de forma concurrente; el "Clojure Way" aprovecha las fortalezas de ambos para brindar un enfoque particularmente poderoso para la programación concurrente.
Si bien la programación funcional funciona increíblemente bien para algunos problemas, algunos tienen la modificación del estado como un elemento fundamental de la solución. Aunque puede ser posible crear una solución funcional a tales problemas, es más fácil pensar en ellos de una manera más tradicional.
Un lenguaje funcional puro no proporciona soporte para datos mutables en absoluto. Clojure, por el contrario, es impuro: proporciona varios tipos diferentes de variables mutables conscientes de la concurrencia, cada una de las cuales es adecuada para diferentes casos de uso. Estos, en conjunto con las estructuras de datos persistentes de Clojure (vamos a cubriremos lo que significa persistente en este contexto más adelante) nos permiten evitar muchos de los problemas que tradicionalmente afligen a los programas concurrentes con estado mutable compartido.
La diferencia entre un lenguaje funcional impuro y un lenguaje imperativo es de énfasis. En un lenguaje imperativo, las variables son mutables por defecto y el código idiomático las modifica con frecuencia. En un lenguaje funcional impuro, las variables son inmutables por defecto y el código idiomático modifica aquellas que no lo son solo cuando es absolutamente necesario. Como veremos, las variables mutables de Clojure nos permiten manejar los efectos secundarios del mundo real sin dejar de ser seguros y consistentes.
Veremos cómo las variables mutables de Clojure funcionan en conjunto con las estructuras de datos persistentes para separar la identidad del estado. Esto permite que varios subprocesos accedan a variables mutables simultáneamente sin bloqueos (y el peligro asociado de interbloqueo) y sin ninguno de los problemas de estado mutable escapado u oculto. Comenzaremos por lo que podría decirse que es el más simple de los tipos de variables mutables de Clojure, el átomo.
Un átomo es una variable atómica (los átomos de Clojure están construidos sobre java.util.concurrent.atomic). A continuación, se muestra un ejemplo de cómo crear y recuperar el valor de un átomo:
user=> (def my-atom (atom 42))
#'user/my-atom
user=> (deref my-atom)
42
user=> @my-atom
42