Come posso utilizzare parMap con una funzione monadica?
-
19-09-2019 - |
Domanda
Ho una funzione getRate monadica:
getRate :: String -> IO Double
Mi piacerebbe mappare questa funzione nel corso di un elenco di stringa di. Normalmente, vorrei solo fare:
mapM getRate ["foo", "bar"]
ma dato che ogni chiamata a getRate rende le chiamate di rete, mi piacerebbe parallelizzare la mappa in modo che ciascuna aliquota viene recuperato in un thread separato (o almeno sparsi tra le code). Sto pensando a qualcosa di simile
parMapM getRate ["foo", "bar"]
, ma non v'è alcuna funzione parMapM e parMap non funziona con funzioni monadici.
Che cosa posso fare?
Soluzione
Si dovrebbe usare Control.Concurrent e sincronizzare intorno ad un Control.Concurrent.MVar; qualcosa di simile:
fork1 :: (a -> IO b) -> a -> IO (MVar b)
fork1 f x =
do
cell <- newEmptyMVar
forkIO (do { result <- f x; putMVar cell result })
return cell
fork :: (a -> IO b) -> [a] -> IO [MVar b]
fork f = mapM (fork1 f)
join :: [MVar b] -> IO [b]
join = mapM takeMVar
forkJoin :: (a -> IO b) -> [a] -> IO [b]
forkJoin f xs = (fork f xs) >>= join
Alcune parti di questo (forchetta, unire) Cerca sequenziale. Ciò che accade in pratica è i fili vengono sparati fuori sequenzialmente in forcella e appuntamento passeggiate attraverso attesa di ciascun filo a turno. Ma l'IO avviene contemporaneamente.
Si noti che se avete bisogno di chiamare funzioni stranieri si dovrebbe usare forkOS invece di forkIO.
Altri suggerimenti
C'è anche un pacchetto monade parallelo che fornisce MAPM :: MonadParallel m => (a -> mb) -> [a] -> m [b] . Guardando l'istanza IO per MonadParallel lo fa allo stesso modo come nella risposta di Dominic.