content: import GetURL
  import TimeIt
  
  import Control.Monad
  import Control.Concurrent
  import Control.Exception
  import Text.Printf
  import qualified Data.ByteString as B
  
  -----------------------------------------------------------------------------
  -- Our Async API:
  
  data Async a = Async (MVar (Either SomeException a))
  
  async :: IO a -> IO (Async a)
  async action = do
    var <- newEmptyMVar
    forkIO (do r <- try action; putMVar var r)  -- <1>
    return (Async var)
  
  waitCatch :: Async a -> IO (Either SomeException a) -- <2>
  waitCatch (Async var) = readMVar var
  
  wait :: Async a -> IO a -- <3>
  wait a = do
    r <- waitCatch a
    case r of
      Left e  -> throwIO e
      Right a -> return a
  
  -- <<waitEither
  waitEither :: Async a -> Async b -> IO (Either a b)
  waitEither a b = do
    m <- newEmptyMVar
    forkIO $ do r <- try (fmap Left  (wait a)); putMVar m r
    forkIO $ do r <- try (fmap Right (wait b)); putMVar m r
    wait (Async m)
  -- >>
  
  -- <<waitAny
  waitAny :: [Async a] -> IO a
  waitAny as = do
    m <- newEmptyMVar
    let forkwait a = forkIO $ do r <- try (wait a); putMVar m r
    mapM_ forkwait as
    wait (Async m)
  -- >>
  
  -----------------------------------------------------------------------------
  
  sites = ["http://www.google.com",
           "http://www.bing.com",
           "http://www.yahoo.com",
           "http://www.wikipedia.com/wiki/Spade",
           "http://www.wikipedia.com/wiki/Shovel"]
  
  -- <<main
  main :: IO ()
  main = do
    let
      download url = do
         r <- getURL url
         return (url, r)
  
    as <- mapM (async . download) sites
  
    (url, r) <- waitAny as
    printf "%s was first (%d bytes)\n" url (B.length r)
    mapM_ wait as
  -- >>
  