11

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

getRate :: String -> IO Double 

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

mapM getRate ["foo", "bar"] 

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

parMapM getRate ["foo", "bar"] 

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

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

ответ

6

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

+0

Это идеальное решение - спасибо! – Bill

6

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