HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

newtypenewtype Concurrently (m :: Type -> Type) a
#

Unlifted Concurrently.

Constructors

Instances5Functor, Applicative, Alternative, Semigroup, Monoid
datadata Conc (m :: Type -> Type) a where
#

A more efficient alternative to Concurrently, which reduces the number of threads that need to be forked. For more information, see this blog post. This is provided as a separate type to Concurrently as it has a slightly different API.

Use the conc function to construct values of type Conc, and runConc to execute the composed actions. You can use the Applicative instance to run different actions and wait for all of them to complete, or the Alternative instance to wait for the first thread to complete.

In the event of a runtime exception thrown by any of the children threads, or an asynchronous exception received in the parent thread, all threads will be killed with an AsyncCancelled exception and the original exception rethrown. If multiple exceptions are generated by different threads, there are no guarantees on which exception will end up getting rethrown.

For many common use cases, you may prefer using helper functions in this module like mapConcurrently.

There are some intentional differences in behavior to Concurrently:

  • Children threads are always launched in an unmasked state, not the inherited state of the parent thread.

Note that it is a programmer error to use the Alternative instance in such a way that there are no alternatives to an empty, e.g. runConc (empty | empty). In such a case, a ConcException will be thrown. If there was an Alternative in the standard libraries without empty, this library would use it instead.

Constructors

Instances5Functor, Applicative, Alternative, Semigroup, Monoid

We want to get rid of the Empty data constructor. We don't want

0 declarations

We want to ensure that there is no nesting of Alt data

0 declarations

We want to ensure that, when racing, we're always racing at least

0 declarations

We want to simplify down to IO.

29 declarations
datadata Flat a
#

Flattened structure, either Applicative or Alternative

Constructors

Instances2Functor, Applicative
  • Functor FlatDefined in unliftio-0.2.25.1 · UnliftIO.Internals.Async
  • Applicative FlatDefined in unliftio-0.2.25.1 · UnliftIO.Internals.Async
datadata FlatApp a where
#

Flattened Applicative. No Alternative stuff directly in here, but may be in the children. Notice this type doesn't have a type parameter for monadic contexts, it hardwires the base monad to IO given concurrency relies eventually on that.

Constructors

Instances2Functor, Applicative
datadata ConcException
#

Things that can go wrong in the structure of a Conc. These are programmer errors.

Instances6Eq, Ord, Show, Generic, Exception, Rep
typetype DList a = [a] -> [a]
#

Simple difference list, for nicer types below

valuepooledMapConcurrentlyN
  1. :: (MonadUnliftIO m, Traversable t)
  2. => Int

    Max. number of threads. Should not be less than 1.

  3. -> (a -> m b)
  4. -> t a
  5. -> m (t b)
#

Like mapConcurrently from async, but instead of one thread per element, it does pooling from a set of threads. This is useful in scenarios where resource consumption is bounded and for use cases where too many concurrent tasks aren't allowed.

Example usage
import Say

action :: Int -> IO Int
action n = do
  tid <- myThreadId
  sayString $ show tid
  threadDelay (2 * 10^6) -- 2 seconds
  return n

main :: IO ()
main = do
  yx <- pooledMapConcurrentlyN 5 (\x -> action x) [1..5]
  print yx

On executing you can see that five threads have been spawned:

$ ./pool
ThreadId 36
ThreadId 38
ThreadId 40
ThreadId 42
ThreadId 44
[1,2,3,4,5]

Let's modify the above program such that there are less threads than the number of items in the list:

import Say

action :: Int -> IO Int
action n = do
  tid <- myThreadId
  sayString $ show tid
  threadDelay (2 * 10^6) -- 2 seconds
  return n

main :: IO ()
main = do
  yx <- pooledMapConcurrentlyN 3 (\x -> action x) [1..5]
  print yx

On executing you can see that only three threads are active totally:

$ ./pool
ThreadId 35
ThreadId 37
ThreadId 39
ThreadId 35
ThreadId 39
[1,2,3,4,5]
valuepooledConcurrently
  1. :: Int

    Max. number of threads. Should not be less than 1.

  2. -> IORef [a]

    Task queue. These are required as inputs for the jobs.

  3. -> (a -> IO ())

    The task which will be run concurrently (but will be pooled properly).

  4. -> IO ()
#

Performs the actual pooling for the tasks. This function will continue execution until the task queue becomes empty. When one of the pooled thread finishes it's task, it will pickup the next task from the queue if an job is available.