Подтвердить что ты не робот

Как я могу использовать parMap с монадической функцией?

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

getRate :: String -> IO Double

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

mapM getRate ["foo", "bar"]

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

parMapM getRate ["foo", "bar"]

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

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

4b9b3361

Ответ 1

Вы должны использовать 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

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

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

Ответ 2

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