packages feed

effectful-cli-0.2.6.0: src/Effectful/CLI/Conc.hs

-- | Concurrency helpers built on "Effectful.Concurrent.Async".
module Effectful.CLI.Conc (
  concurrently,
  concurrently_,
  runConcurrently,
  runConcurrently_,
  mapConcurrently,
  mapConcurrently_,
  forConcurrently,
  forConcurrently_,
  batchConcurrently,
  batchConcurrently_,
  batchConcurrentlyWithIndex,
  batchConcurrentlyWithIndex_,
) where

import Data.Foldable (toList)
import Data.Traversable (mapAccumL)
import Effectful (Eff, (:>))
import Effectful.Concurrent.Async (
  Concurrent,
  pooledMapConcurrentlyN,
  pooledMapConcurrentlyN_,
 )
import Effectful.Concurrent.Async qualified as Async

-- | Run two actions concurrently and return both results.
concurrently
  :: Concurrent :> es
  => Eff es a
  -- ^ first action
  -> Eff es b
  -- ^ second action
  -> Eff es (a, b)
concurrently = Async.concurrently

-- | Like 'concurrently', discarding results.
concurrently_
  :: Concurrent :> es
  => Eff es a
  -- ^ first action
  -> Eff es b
  -- ^ second action
  -> Eff es ()
concurrently_ = Async.concurrently_

-- | Map an action over a container concurrently, without a concurrency limit.
mapConcurrently
  :: Concurrent :> es
  => Traversable t
  => (a -> Eff es b)
  -- ^ action for each element
  -> t a
  -- ^ elements
  -> Eff es (t b)
mapConcurrently = Async.mapConcurrently

-- | Like 'mapConcurrently', discarding results.
mapConcurrently_
  :: Concurrent :> es
  => Foldable f
  => (a -> Eff es b)
  -- ^ action for each element
  -> f a
  -- ^ elements
  -> Eff es ()
mapConcurrently_ = Async.mapConcurrently_

-- | 'mapConcurrently' with its arguments flipped.
forConcurrently
  :: Concurrent :> es
  => Traversable t
  => t a
  -- ^ elements
  -> (a -> Eff es b)
  -- ^ action for each element
  -> Eff es (t b)
forConcurrently = Async.forConcurrently

-- | Like 'forConcurrently', discarding results.
forConcurrently_
  :: Concurrent :> es
  => Foldable f
  => f a
  -- ^ elements
  -> (a -> Eff es b)
  -- ^ action for each element
  -> Eff es ()
forConcurrently_ = Async.forConcurrently_

-- | Run all actions concurrently and collect their results.
--
-- @since 0.1.0.0
runConcurrently
  :: Concurrent :> es
  => Traversable t
  => t (Eff es a)
  -- ^ actions
  -> Eff es (t a)
runConcurrently = mapConcurrently id

-- | Run all actions concurrently, discarding results.
--
-- @since 0.1.0.0
runConcurrently_
  :: Concurrent :> es
  => Foldable t
  => t (Eff es a)
  -- ^ actions
  -> Eff es ()
runConcurrently_ = mapConcurrently_ id

-- | Map over a list with at most @n@ concurrent actions.
--
-- @since 0.1.0.0
batchConcurrently
  :: Concurrent :> es
  => Traversable t
  => Int
  -- ^ maximum number of concurrent actions
  -> (a -> Eff es b)
  -- ^ action for each element
  -> t a
  -- ^ elements
  -> Eff es (t b)
batchConcurrently = pooledMapConcurrentlyN

-- | Like 'batchConcurrently', discarding results.
--
-- @since 0.1.0.0
batchConcurrently_
  :: Concurrent :> es
  => Foldable f
  => Int
  -- ^ maximum number of concurrent actions
  -> (a -> Eff es b)
  -- ^ action for each element
  -> f a
  -- ^ elements
  -> Eff es ()
batchConcurrently_ = pooledMapConcurrentlyN_

-- | Like 'batchConcurrently', also passing each element's 1-based index.
--
-- @since 0.1.0.0
batchConcurrentlyWithIndex
  :: Concurrent :> es
  => Traversable t
  => Int
  -- ^ maximum number of concurrent actions
  -> (Int -> a -> Eff es b)
  -- ^ action for each element
  -> t a
  -- ^ elements
  -> Eff es (t b)
batchConcurrentlyWithIndex n f =
  batchConcurrently n (uncurry f) . snd . mapAccumL (\i x -> (i + 1, (i, x))) 1

-- | Like 'batchConcurrentlyWithIndex', discarding results.
--
-- @since 0.1.0.0
batchConcurrentlyWithIndex_
  :: Concurrent :> es
  => Foldable f
  => Int
  -- ^ maximum number of concurrent actions
  -> (Int -> a -> Eff es b)
  -- ^ action for each element
  -> f a
  -- ^ elements
  -> Eff es ()
batchConcurrentlyWithIndex_ n f = batchConcurrently_ n (uncurry f) . zip [1 ..] . toList