I have a program which, at its core, involves repeatedly performing an action on some value, and based on that action also returning a list of new values to perform that action on. The pure version of my function is:

recurse :: (a -> [a]) -> a -> [a]
recurse f a = a : concatMap (recurse f) (f a)

Which can be used for things like tree traversals, such as:

data Tree a = Node [Tree a] | Leaf a

f (Node ls) = ls
f (Leaf x) = []

recurse f (Node [Leaf 10, Node [Leaf 20, Leaf 3], Leaf 12]) 
  == [Node [..], Leaf 10, Node [..], Leaf 20, Leaf 3, Leaf 12]

This function becomes more useful if you generalize it to an arbitrary monad:

recurseM :: Monad m => (a -> m [a]) -> a -> m [a]
recurseM f a = do
  xs <- f a
  rests <- mapM (recurseM f) xs
  return $ a : concat rests

But what I really want to do with it is make it asynchronous, because (a) I do not care about the order the results come in, and (b) I anticipate the actions themselves taking a while waiting on network operations. Here is my first pass at writing the async implementation (without results) of the above functions:

recurseAsync_ :: (a -> IO [a]) -> a -> IO ()
recurseAsync_ f a = do
  xs <- f a
  mapM_ (forkIO . recurseAsync_ f) xs

While this works, I'm unfamiliar enough with concurrency in general and in Haskell specifically to be sure that this will actually run all of the code I want in parallel. Also, I'm unsure of how to make this code return the results, but that isn't really the important part of the code.


I think yours works, but async specializes in this stuff.

recurseAsync :: (a -> IO [a]) -> a -> IO [a]
recurseAsync f a = do
  xs <- f a
  fmap concat $ mapConcurrently (recurseAsync f) xs

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.