2012-03-22 18 views
5

En another question, pregunté acerca de las multimapas concurrentes para Java.Funcionalmente equivalente a la multimapa concurrente

¿Hay algún patrón de programación funcional (inmutable) que pueda usarse en su lugar en un programa Scala o Clojure? Me imagino que la solución de Scala probablemente involucre a actores y la de Clojure a atom, ref o agent, pero puede haber una manera mejor. Dado que ambos lenguajes permiten "retroceder" a la interoperabilidad de Java y solo al usar una solución Java, puedo usar cualquier respuesta que obtenga a mi primera pregunta, pero eso no se apegará al paradigma de programación funcional. ¿Cómo lo resuelven los programadores de Haskell?

+1

Es un error común que Haskell no permite la mutación. Lo hace. El problema es que todas las mutaciones deben anotarse debidamente en los tipos; por lo tanto, la forma típica de componer juntas las operaciones de mutación es con la interfaz de mónada. Los Haskeller pueden "retroceder" a la mutabilidad casi de la misma manera que Scala y Clojure, excepto que en Haskell no se puede hacer trampas y hacer que parezca que una función es pura cuando no lo es. Sin embargo, si puede encapsular por completo la mutación en su función, entonces aún puede exponer una interfaz pura. –

Respuesta

7

Los mapas y conjuntos de Clojure estándar son inmutables (y persistentes) [1], por lo que funcionan igual de bien en programas simultáneos. Es posible que desee almacenarlos en un ref/agent/var/atom dependiendo de sus requisitos, y puede simplemente actualizar el ref/agent/var/atom como siempre.

Usted puede tener un mapa más mutable, si los valores son en realidad los árbitros, así:

{:a (ref #{1 2 3}) 
:b (ref #{4 5 6})} 

En este caso, usted será capaz de agregar realmente a los valores clave (en la transacción de que ya existen curso). Adición y eliminación de claves seguirá devolviendo nuevos mapas, que comparten los mismos árbitros como los mapas originales, por lo que los cambios en uno de ellos será visible para los demás:

user=> (def mmap {:a (ref #{1 2 3}) :b (ref #{4 5 6})}) 
#'user/mmap 
user=> mmap 
{:a #<[email protected]: #{1 2 3}>, :b #<[email protected]: #{4 5 6}>} 
user=> (def mmap2 (assoc mmap :c (ref #{7 8 9}))) 
#'user/mmap2 
user=> mmap2 
{:C#<[email protected]: #{7 8 9}>, :a #<[email protected]: #{1 2 3}>, :b #<[email protected]: #{4 5 6}>} 
user=> mmap 
{:a #<[email protected]: #{1 2 3}>, :b #<[email protected]: #{4 5 6}>} 
user=> (dosync (alter (:a mmap2) conj 0)) 
#{0 1 2 3} 
user=> mmap 
{:a #<[email protected]: #{0 1 2 3}>, :b #<[email protected]: #{4 5 6}>} 
user=> mmap2 
{:C#<[email protected]: #{7 8 9}>, :a #<[email protected]: #{0 1 2 3}>, :b #<[email protected]: #{4 5 6}>} 

[1] Es decir, la adición/eliminar/modificar claves, y los valores realmente devuelven un nuevo mapa, sin cambiar el original.

3

Una estructura de datos funcional es una estructura de datos inmutables. Las estructuras de datos inmutables no tienen problemas con la concurrencia porque no se pueden modificar.

+0

Es un Actor (o clase similar a un actor) la única forma segura de gestionar esta estructura de datos. – Ralph

+2

@Ralph en absoluto. Los actores son útiles, pero no son un tema general para cualquier problema de concurrencia. Como dice Daniel, no necesitas ningún tipo de gestión de una estructura inmutable. La pregunta entonces es si necesitas mutabilidad, y si es así de qué manera. Más detalles ayudarían aquí. – Submonoid

Cuestiones relacionadas