reservadas - ¿Cómo puedo emular los canales de Go con Haskell?
peta haskell en español (2)
Hace poco empecé a leer sobre el lenguaje de programación Go y encontré que las variables de canal eran un concepto muy atractivo. ¿Es posible emular el mismo concepto en Haskell? Tal vez tener un tipo de datos Channel a
y una estructura de mónada para habilitar el estado mutable y funciones que funcionan como la palabra clave.
No soy muy bueno en programación concurrente y un simple mecanismo de paso de canal como este en Haskell realmente me facilitaría la vida.
EDITAR
La gente me pidió que aclarara qué tipo de patrones de Go me interesaba traducir a Haskell. Así que Go tiene variables de canal que son de primera clase y se pueden pasar y devolver por funciones. Puedo leer y escribir en estos canales, y me comunico fácilmente entre las rutinas que pueden ejecutarse simultáneamente. Go también tiene una palabra clave go
, que de acuerdo con la especificación del idioma inicia la ejecución de una función simultáneamente como un hilo independiente y continúa ejecutando el código sin esperar.
El patrón exacto en el que estoy interesado es algo como esto (la sintaxis de Go es rara: las variables se declaran con varName varType en lugar de la forma invertida habitual, pero creo que es legible):
func generateStep(ch chan int) {
//ch is a variable of type chan int, which is a channel that comunicate integers
for {
ch <- randomInteger() //just sends random integers in the channel
}
func filter(input, output chan int) {
state int
for {
step <- input //reads an int from the input channel
newstate := update(state, step) //update the variable with some update function
if criteria(newstate, state) {
state = newstate // if the newstate pass some criteria, accept the update
}
output <- state //pass it to the output channel
}
}
func main() {
intChan := make(chan int)
mcChan := make(chan int)
go generateStep(intChan) // execute the channels concurrently
go filter(intChan, mcChan)
for i:=0; i<numSteps; i++ {
x <- mcChan // get values from the filtered channel
accumulateStats(x) // calculate some statistics
}
printStatisticsAbout(x)
}
Mi interés principal es hacer simulaciones de Monte Carlo, en las que genero configuraciones de forma secuencial al intentar modificar el estado actual del sistema y aceptar la modificación si cumple con algunos criterios.
El hecho de usar esas cosas del canal podría escribir una simulación de Monte Carlo muy simple, legible y pequeña que funcionaría en paralelo en mi procesador multinúcleo realmente me impresionó.
El problema es que Go tiene algunas limitaciones (especialmente, carece de polimorfismo en la forma en que estoy acostumbrado en Haskell), y además de eso, realmente me gusta Haskell y no quiero intercambiarlo. Entonces, la pregunta es si hay alguna forma de usar algunas mecánicas que se parecen al código anterior para hacer una simulación concurrente en Haskell fácilmente.
EDITAR (2, contexto): No he aprendido en Ciencias de la Computación, especialmente en concurrencia. Solo soy un tipo que crea programas simples para resolver problemas simples en mi rutina diaria de investigación en una disciplina que no tiene nada que ver con la CS. Me parece que la forma en que Haskell funciona es interesante y me gusta usarla para hacer mis pequeñas tareas.
Nunca he oído hablar solo de los canales de cálculo de pi o CSP. Lo siento si la pregunta parece mal planteada, probablemente es culpa de mi gran ignorancia acerca de la materia.
Tienes razón, debería ser más específico sobre qué patrón en Go me gustaría replicar en Haskell, e intentaré editar la pregunta para que sea más específica. Pero no esperes preguntas teóricas profundas. El problema es que, de las pocas cosas que leí y codifiqué, parece que Go tiene una buena forma de hacer concurrencia (y en mi caso, esto significa que mi tarea de hacer que todos mis núcleos vibren con cálculos numéricos es más fácil), y Si pudiera usar una sintaxis similar en Haskell, me alegraría.
Creo que lo que estás buscando es Control.Concurrent.Chan de Base. No he encontrado que sea diferente de los chats de Go que no sean las obvias haskellifications. Los canales no son algo especial, eche un vistazo a la página wiki al respecto .
Los canales forman parte de un concepto más general denominado procesos secuenciales de comunicación (CSP) , y si desea realizar una programación al estilo de CSP en Haskell, puede consultar el paquete Communicating Haskell Processes (CHP) .
CHP es solo una forma de hacer concurrencia en Haskell, eche un vistazo a la página de concurrencia de Haskellwiki para obtener más información. Creo que su caso de uso podría escribirse mejor usando Data Parrallel Haskell , sin embargo, actualmente es un trabajo en progreso, por lo que es posible que desee utilizar otra cosa por ahora.
Extendiendo la respuesta de HaskellElephant, Control.Concurrent.Chan es el camino a seguir para los canales y Control.Concurrent''s forkIO
puede emular la palabra clave go
. Para hacer que la sintaxis sea más parecida a Go, se puede usar este conjunto de alias:
import Control.Concurrent (forkIO)
import Control.Concurrent.Chan (newChan, readChan, writeChan)
import Control.Concurrent.MVar (newMVar, swapMVar, readMVar)
data GoChan a = GoChan { chan :: Chan a, closed :: MVar Bool }
go :: IO () -> IO ThreadId
go = forkIO
make :: IO (GoChan a)
make = do
ch <- newChan
cl <- newMVar False
return $ GoChan ch cl
get :: GoChan a -> IO a
get ch = do
cl <- readMVar $ closed ch
if cl
then error "Can''t read from closed channel!"
else readChan $ chan ch
(=->) :: a -> GoChan a -> IO ()
v =-> ch = do
cl <- readMVar $ closed ch
if cl
then error "Can''t write to closed channel!"
else writeChan (chan ch) v
forRange :: GoChan a -> (a -> IO b) -> IO [b]
forRange ch func = fmap reverse $ range_ ch func []
where range_ ch func acc = do
cl <- readMVar $ closed ch
if cl
then return ()
else do
v <- get ch
func v
range_ ch func $ v : acc
close :: GoChan a -> IO ()
close ch = do
swapMVar (closed ch) True
return ()
Esto puede ser usado como tal:
import Control.Monad
generate :: GoChan Int -> IO ()
generate c = do
forM [1..100] (=-> c)
close c
process :: GoChan Int -> IO ()
process c = forRange c print
main :: IO ()
main = do
c <- make
go $ generate c
process c
(Advertencia: código no probado)