Skip to content

Instantly share code, notes, and snippets.

@evincarofautumn
Last active September 15, 2019 07:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evincarofautumn/477ac41e1f7dc46850b085ca2e800412 to your computer and use it in GitHub Desktop.
Save evincarofautumn/477ac41e1f7dc46850b085ca2e800412 to your computer and use it in GitHub Desktop.
Poll asyncs until any fails or all return and only one succeeds
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ScopedTypeVariables #-}
oneOfMany :: forall a. [Async (Maybe a)] -> IO (Either SomeException (Maybe a))
oneOfMany asyncs = loop
where
loop :: IO (Either SomeException (Maybe a))
loop = do
statuses <- for asyncs poll
let exceptions = [exception | Just (Left exception) <- statuses]
case exceptions of
-- At least one task failed: cancel all tasks and fail.
exception : _ -> do
for_ asyncs cancel
pure (Left exception)
-- No tasks have yet failed.
[] -> let
-- Collect results of all tasks.
allResults :: Maybe [Maybe a]
allResults = for statuses \ status -> do
Right result <- status
pure result
in case allResults of
-- If at least one task hasn't completed, continue polling.
Nothing -> loop
-- All tasks have completed.
Just results -> case catMaybes results of
-- If exactly one task returned a result, return.
[value] -> pure (Right (Just value))
-- If no task returned a result, done.
[] -> pure (Right Nothing)
-- If multiple tasks returned, error.
_ -> throwIO (InternalError "at most one task should return")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment