Как я могу использовать parMap с монадической функцией? - PullRequest
11 голосов
/ 10 февраля 2010

У меня есть монадическая функция getRate:

getRate :: String -> IO Double

Я бы хотел сопоставить эту функцию со списком строк. Обычно я бы просто сделал:

mapM getRate ["foo", "bar"]

но поскольку каждый вызов getRate выполняет сетевые вызовы, я бы хотел распараллелить карту, чтобы каждая скорость выбиралась в отдельном потоке (или, по крайней мере, распределялась по очередям). Я думаю о чем-то вроде

parMapM getRate ["foo", "bar"]

но функции parMapM нет, и parMap не работает с монадическими функциями.

Что я могу сделать?

Ответы [ 2 ]

7 голосов
/ 10 февраля 2010

Вы должны использовать Control.Concurrent и синхронизироваться вокруг Control.Concurrent.MVar; что-то вроде:

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

Части этого (форк, соединение) выглядят последовательно. На практике происходит то, что потоки запускаются последовательно в форке, и рандеву проходит по очереди, ожидая каждого потока по очереди. Но IO происходит одновременно.

Обратите внимание, что если вам нужно вызывать сторонние функции, вы должны использовать forkOS вместо forkIO.

6 голосов
/ 24 июня 2010

Существует также параллельный монадный пакет, который обеспечивает mapM :: MonadParallel m => (a -> m b) -> [a] -> m [b] . Глядя на экземпляр ввода-вывода для MonadParallel, он делает это так же, как в ответе Доминика.

...