开发者

How can I use parMap with a monadic function?

开发者 https://www.devze.com 2022-12-19 13:17 出处:网络
I have a monadic function getRate: getRate :: String -> 开发者_运维问答IO Double I\'d like to map this function over a list of String\'s.Normally, I would just do:

I have a monadic function getRate:

getRate :: String -> 开发者_运维问答IO Double

I'd like to map this function over a list of String's. Normally, I would just do:

mapM getRate ["foo", "bar"]

but since each call to getRate makes network calls, I'd like to parallelize the map so that each rate is fetched in a separate thread (or at least spread out among queues). I'm thinking of something like

parMapM getRate ["foo", "bar"]

but there is no parMapM function and parMap doesn't work with monadic functions.

What can I do?


You should use Control.Concurrent and synchronize around a Control.Concurrent.MVar; something like:

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

Parts of this (fork, join) look sequential. What's happening in practice is the threads are fired off sequentially in fork and rendezvous walks through waiting for each thread in turn. But the IO happens concurrently.

Note that if you need to call foreign functions you should use forkOS instead of forkIO.


There is also a monad-parallel package which provides mapM :: MonadParallel m => (a -> m b) -> [a] -> m [b]. Looking at the IO instance for MonadParallel it does it the same way as in Dominic's answer.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号