У меня есть одноместная функция getRate:
getRate :: String -> IO Double
Я хотел бы отобразить эту функцию по списку Строки. Обычно, я просто сделал бы:
mapM getRate ["foo", "bar"]
но так как каждый вызов к getRate выполняет сетевые вызовы, я хотел бы параллелизировать карту так, чтобы каждый уровень был выбран в отдельном потоке (или по крайней мере распространен среди очередей). Я думаю о чем-то как
parMapM getRate ["foo", "bar"]
но нет никакой функции parMapM, и parMap не работает с одноместными функциями.
Что я могу сделать?
Вы должны использовать 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) выглядят последовательными. На практике потоки запускаются последовательно в fork, а rendezvous проходит через ожидание каждого потока по очереди. Но ввод-вывод происходит параллельно.
Обратите внимание, что если вам нужно вызывать посторонние функции, вы должны использовать forkOS вместо forkIO.
Существует также пакет с монадным параллелизмом, который предоставляет mapM :: MonadParallel m => (a -> m b) -> [a] -> m [b] . Глядя на экземпляр IO для MonadParallel, он делает это так же, как в ответе Доминика.