Re: Proposal: Control.Concurrent.Async

On Thu, 14 Jun 2012 12:42:51 0100, Simon Marlow
<- runConcurrently $ (,,) <$> Concurrently (getURL "url1") <*> Concurrently (getURL "url2") <*> Concurrently (getURL "url3")
More code here: https://gist.github.com/2926572
I'm not sure about this. What you get with the above code is a strange nesting of concurrently calls, whereas what the user might expect is for it to behave like the existing concurrently but on 3-tuples instead of pairs.
Actually, that is what I expected too. So, this is not the way to use concurrently? concurrently3 m1 m2 m3 = (((v1, v2), v3) -> (v1, v2, v3)) <$> concurrently (concurrently m1 m2) m3 Would this be the correct implementation then: concurrently3 m1 m2 m3 = withAsync m1 $ \a1 -> withAsync m2 $ \a2 -> withAsync m3 $ \a3 -> wait3 a1 a2 a3 And can I then write wait3 as wait3 a1 a2 a3 = (((v1, v2), v3) -> (v1, v2, v3)) <$> do withAsync (waitBoth a1 a2) $ \a12 -> waitBoth a12 a3 or does it have to be: wait3 a1 a2 a3 = atomically $ do v1 <- waitSTM a1 `orElse` (waitSTM a2 >> waitSTM a3 >> retry) v2 <- waitSTM a2 `orElse` (waitSTM a3 >> retry) v3 <- waitSTM a3 return (v1, v2, v3) If it is this complicated, then (properly written) Applicative and Alternative instances are certainly useful.
I like the idea of generalising to arbitrary Traversable structures though. Maybe we could provide something like
doConcurrently :: Traversable t => t (IO a) -> IO (t a)
I haven't tried to write it, but it looks like it ought to be possible.
You'll need an Applicative instance for this anyway. -- Sjoerd Visscher https://github.com/sjoerdvisscher/blog

On 14/06/2012 13:12, Sjoerd Visscher wrote:
On Thu, 14 Jun 2012 12:42:51 0100, Simon Marlow
wrote: On 13/06/2012 22:58, Sjoerd Visscher wrote: (page1, page2, page3) <- runConcurrently $ (,,) <$> Concurrently (getURL "url1") <*> Concurrently (getURL "url2") <*> Concurrently (getURL "url3")
More code here: https://gist.github.com/2926572
I'm not sure about this. What you get with the above code is a strange nesting of concurrently calls, whereas what the user might expect is for it to behave like the existing concurrently but on 3-tuples instead of pairs.
Actually, that is what I expected too. So, this is not the way to use concurrently?
concurrently3 m1 m2 m3 = (((v1, v2), v3) -> (v1, v2, v3)) <$> concurrently (concurrently m1 m2) m3
So what bothered me about this is that it makes 4 threads when I would expect 3, and also the asymmetry looks strange. However see below.
or does it have to be:
wait3 a1 a2 a3 = atomically $ do v1 <- waitSTM a1 `orElse` (waitSTM a2 >> waitSTM a3 >> retry) v2 <- waitSTM a2 `orElse` (waitSTM a3 >> retry) v3 <- waitSTM a3 return (v1, v2, v3)
I had in mind this: waitList :: [Async a] -> IO [a] waitList xs = atomically $ do foldr orElse retry (map (void.waitSTM) xs) mapM waitSTM xs but, the problem is that this is an O(N)-sized transaction and it will be run O(N) times before it commits, giving us O(N^2). I've just tested it and it scales terribly, the nested version is much better. So that's an interesting discovery! I still need to ponder the Applicative version, I'm not familiar with Control.Newtype. I would still like the Traversable abstraction I mentioned in my last message. Cheers, Simon

On 15/06/12 10:07, Simon Marlow wrote:
On 14/06/2012 13:12, Sjoerd Visscher wrote:
On Thu, 14 Jun 2012 12:42:51 0100, Simon Marlow
wrote: On 13/06/2012 22:58, Sjoerd Visscher wrote: (page1, page2, page3) <- runConcurrently $ (,,) <$> Concurrently (getURL "url1") <*> Concurrently (getURL "url2") <*> Concurrently (getURL "url3")
More code here: https://gist.github.com/2926572
I'm not sure about this. What you get with the above code is a strange nesting of concurrently calls, whereas what the user might expect is for it to behave like the existing concurrently but on 3-tuples instead of pairs.
Actually, that is what I expected too. So, this is not the way to use concurrently?
concurrently3 m1 m2 m3 = (((v1, v2), v3) -> (v1, v2, v3)) <$> concurrently (concurrently m1 m2) m3
So what bothered me about this is that it makes 4 threads when I would expect 3, and also the asymmetry looks strange. However see below.
For your enjoyment / horror, I present my implementation of Concurrently that uses a single MVar, and exactly one thread per concurrent action. The code is at https://gist.github.com/2938456 . It uses an MVar (IO (Maybe a)), i.e. each worker thread can post a new action that can be done to combine the results; and in the end you hopefully get a value of type a. I'm sure that the code could be improved a lot. Twan
participants (3)
-
Simon Marlow
-
Sjoerd Visscher
-
Twan van Laarhoven