packages feed

polysemy-conc-0.2.0.0: lib/Polysemy/Conc/Race.hs

{-# options_haddock prune #-}
-- |Description: Race interpreters
module Polysemy.Conc.Race where

import qualified Control.Concurrent.Async as Async
import Polysemy.Final (getInitialStateS, interpretFinal, runS)
import qualified Polysemy.Time as Time
import Polysemy.Time (MicroSeconds (MicroSeconds), TimeUnit)
import qualified System.Timeout as System

import qualified Polysemy.Conc.Data.Race as Race
import Polysemy.Conc.Data.Race (Race)

biseqEither ::
  Functor f =>
  Either (f a) (f b) ->
  f (Either a b)
biseqEither =
  either (fmap Left) (fmap Right)
{-# inline biseqEither #-}

-- |Interpret 'Race' in terms of 'Async.race' and 'System.timeout'.
-- Since this has to pass higher-order thunks as 'IO' arguments, it is interpreted in terms of 'Final IO'.
interpretRace ::
  Member (Final IO) r =>
  InterpreterFor Race r
interpretRace =
  interpretFinal @IO \case
    Race.Race left right ->
      fmap (fmap biseqEither) . Async.race <$> runS left <*> runS right
    Race.Timeout err (Time.convert -> MicroSeconds timeout) mb -> do
      mbT <- runS mb
      s <- getInitialStateS
      pure (maybe (Left err <$ s) (fmap Right) <$> System.timeout (fromIntegral timeout) mbT)
{-# inline interpretRace #-}

-- |Specialization of 'Race.race' for the case where both thunks return the same type, obviating the need for 'Either'.
race_ ::
  Member Race r =>
  Sem r a ->
  Sem r a ->
  Sem r a
race_ ml mr =
  unify <$> Race.race ml mr
{-# inline race_ #-}

-- |Specialization of 'Race.timeout' for the case where the thunk return the same type as the fallback, obviating the
-- need for 'Either'.
timeout_ ::
  TimeUnit u =>
  Member Race r =>
  a ->
  u ->
  Sem r a ->
  Sem r a
timeout_ err interval ma =
  unify <$> Race.timeout err interval ma
{-# inline timeout_ #-}

-- |Specialization of 'Race.timeout' for unit actions.
timeoutU ::
  TimeUnit u =>
  Member Race r =>
  u ->
  Sem r () ->
  Sem r ()
timeoutU =
  timeout_ ()
{-# inline timeoutU #-}