polysemy-conc 0.12.1.0 → 0.15.0.0
raw patch · 54 files changed
Files
- LICENSE +1/−1
- changelog.md +7/−0
- lib/Polysemy/Conc.hs +6/−88
- lib/Polysemy/Conc/Async.hs +9/−9
- lib/Polysemy/Conc/AtomicState.hs +2/−2
- lib/Polysemy/Conc/Data/QueueResult.hs +5/−5
- lib/Polysemy/Conc/Effect/Critical.hs +6/−6
- lib/Polysemy/Conc/Effect/Events.hs +6/−6
- lib/Polysemy/Conc/Effect/Gate.hs +4/−4
- lib/Polysemy/Conc/Effect/Interrupt.hs +0/−44
- lib/Polysemy/Conc/Effect/Lock.hs +8/−8
- lib/Polysemy/Conc/Effect/Mask.hs +23/−16
- lib/Polysemy/Conc/Effect/Monitor.hs +11/−11
- lib/Polysemy/Conc/Effect/Queue.hs +12/−12
- lib/Polysemy/Conc/Effect/Race.hs +6/−6
- lib/Polysemy/Conc/Effect/Scoped.hs +0/−155
- lib/Polysemy/Conc/Effect/Semaphore.hs +6/−6
- lib/Polysemy/Conc/Effect/Sync.hs +13/−13
- lib/Polysemy/Conc/Effect/SyncRead.hs +6/−6
- lib/Polysemy/Conc/Events.hs +17/−17
- lib/Polysemy/Conc/Gate.hs +1/−1
- lib/Polysemy/Conc/Interpreter/Critical.hs +3/−3
- lib/Polysemy/Conc/Interpreter/Events.hs +5/−5
- lib/Polysemy/Conc/Interpreter/Gate.hs +3/−3
- lib/Polysemy/Conc/Interpreter/Interrupt.hs +0/−243
- lib/Polysemy/Conc/Interpreter/Lock.hs +4/−4
- lib/Polysemy/Conc/Interpreter/Mask.hs +26/−12
- lib/Polysemy/Conc/Interpreter/Monitor.hs +24/−14
- lib/Polysemy/Conc/Interpreter/Queue/Pure.hs +5/−5
- lib/Polysemy/Conc/Interpreter/Queue/TB.hs +4/−4
- lib/Polysemy/Conc/Interpreter/Queue/TBM.hs +4/−4
- lib/Polysemy/Conc/Interpreter/Race.hs +2/−2
- lib/Polysemy/Conc/Interpreter/Scoped.hs +0/−640
- lib/Polysemy/Conc/Interpreter/Semaphore.hs +5/−5
- lib/Polysemy/Conc/Interpreter/Stack.hs +6/−8
- lib/Polysemy/Conc/Interpreter/Sync.hs +6/−6
- lib/Polysemy/Conc/Interpreter/SyncRead.hs +2/−2
- lib/Polysemy/Conc/Monitor.hs +9/−13
- lib/Polysemy/Conc/Queue.hs +5/−5
- lib/Polysemy/Conc/Queue/Result.hs +1/−1
- lib/Polysemy/Conc/Queue/Timeout.hs +2/−2
- lib/Polysemy/Conc/Race.hs +8/−8
- lib/Polysemy/Conc/Retry.hs +7/−7
- lib/Polysemy/Conc/Semaphore.hs +1/−1
- lib/Polysemy/Conc/Sync.hs +12/−12
- lib/Polysemy/Conc/SyncRead.hs +3/−3
- polysemy-conc.cabal +40/−100
- readme.md +1/−1
- test/Main.hs +21/−22
- test/Polysemy/Conc/Test/InterruptTest.hs +0/−31
- test/Polysemy/Conc/Test/LockTest.hs +4/−3
- test/Polysemy/Conc/Test/MaskTest.hs +2/−2
- test/Polysemy/Conc/Test/MonitorTest.hs +112/−25
- test/Polysemy/Conc/Test/Run.hs +14/−0
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2020 Torsten Schmits+Copyright (c) 2023 Torsten Schmits Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
changelog.md view
@@ -1,3 +1,10 @@+# 0.14.0.0++## Breaking++* Move `Interrupt` to `polysemy-process` to avoid the dependency on `unix`.+* Remove deprecated `Scoped`.+ # 0.12.0.0 ## Breaking
lib/Polysemy/Conc.hs view
@@ -1,4 +1,4 @@--- |Description: Polysemy Effects for Concurrency+-- | Description: Polysemy Effects for Concurrency module Polysemy.Conc ( -- * Introduction -- $intro@@ -79,15 +79,6 @@ -- ** Interpreters interpretRace, - -- * Signal Handling- -- $signal- Interrupt,-- -- ** Interpreters- interpretInterrupt,- interpretInterruptOnce,- interpretInterruptNull,- -- * Event Channels Events, Consume,@@ -124,6 +115,7 @@ -- * Masking Mask,+ MaskMode (..), UninterruptibleMask, mask, uninterruptibleMask,@@ -136,42 +128,6 @@ interpretMaskPure, interpretUninterruptibleMaskPure, - -- * Scoped Effects- Scoped,- Scoped_,- scoped,- scoped_,- rescope,-- -- ** Interpreters- interpretScoped,- interpretScopedH,- interpretScopedH',- interpretScopedAs,- interpretScopedWith,- interpretScopedWithH,- interpretScopedWith_,- runScoped,- runScopedAs,- interpretScopedResumable,- interpretScopedResumableH,- interpretScopedResumable_,- interpretScopedResumableWith,- interpretScopedResumableWithH,- interpretScopedResumableWith_,- interpretResumableScoped,- interpretResumableScopedH,- interpretResumableScoped_,- interpretResumableScopedWith,- interpretResumableScopedWithH,- interpretResumableScopedWith_,- interpretScopedR,- interpretScopedRH,- interpretScopedR_,- interpretScopedRWith,- interpretScopedRWithH,- interpretScopedRWith_,- -- * Monitoring Monitor, monitor,@@ -232,13 +188,11 @@ import Polysemy.Conc.Effect.Critical (Critical) import Polysemy.Conc.Effect.Events (Consume, Events, consume, publish, subscribe) import Polysemy.Conc.Effect.Gate (Gate, Gates)-import Polysemy.Conc.Effect.Interrupt (Interrupt) import Polysemy.Conc.Effect.Lock (Lock, lock, lockOr, lockOrSkip, lockOrSkip_)-import Polysemy.Conc.Effect.Mask (Mask, Restoration, UninterruptibleMask, mask, restore, uninterruptibleMask)+import Polysemy.Conc.Effect.Mask (Mask, MaskMode (..), Restoration, UninterruptibleMask, mask, restore, uninterruptibleMask) import Polysemy.Conc.Effect.Monitor (Monitor, Restart, RestartingMonitor, ScopedMonitor, monitor, restart, withMonitor) import Polysemy.Conc.Effect.Queue (Queue) import Polysemy.Conc.Effect.Race (Race, race, timeout)-import Polysemy.Conc.Effect.Scoped (Scoped, Scoped_, rescope, scoped, scoped_) import Polysemy.Conc.Effect.Semaphore (Semaphore) import Polysemy.Conc.Effect.Sync (ScopedSync, Sync) import Polysemy.Conc.Effect.SyncRead (SyncRead)@@ -263,7 +217,6 @@ import Polysemy.Conc.Interpreter.Critical (interpretCritical, interpretCriticalNull) import Polysemy.Conc.Interpreter.Events (EventConsumer, interpretEventsChan) import Polysemy.Conc.Interpreter.Gate (interpretGate, interpretGates)-import Polysemy.Conc.Interpreter.Interrupt (interpretInterrupt, interpretInterruptNull, interpretInterruptOnce) import Polysemy.Conc.Interpreter.Lock (interpretLockPermissive, interpretLockReentrant) import Polysemy.Conc.Interpreter.Mask ( interpretMaskFinal,@@ -281,35 +234,6 @@ import Polysemy.Conc.Interpreter.Queue.TB (interpretQueueTB) import Polysemy.Conc.Interpreter.Queue.TBM (interpretQueueTBM) import Polysemy.Conc.Interpreter.Race (interpretRace)-import Polysemy.Conc.Interpreter.Scoped (- interpretResumableScoped,- interpretResumableScopedH,- interpretResumableScopedWith,- interpretResumableScopedWithH,- interpretResumableScopedWith_,- interpretResumableScoped_,- interpretScoped,- interpretScopedAs,- interpretScopedH,- interpretScopedH',- interpretScopedR,- interpretScopedRH,- interpretScopedRWith,- interpretScopedRWithH,- interpretScopedRWith_,- interpretScopedR_,- interpretScopedResumable,- interpretScopedResumableH,- interpretScopedResumableWith,- interpretScopedResumableWithH,- interpretScopedResumableWith_,- interpretScopedResumable_,- interpretScopedWith,- interpretScopedWithH,- interpretScopedWith_,- runScoped,- runScopedAs,- ) import Polysemy.Conc.Interpreter.Semaphore (interpretSemaphoreQ, interpretSemaphoreT) import Polysemy.Conc.Interpreter.Stack (ConcStack, runConc) import Polysemy.Conc.Interpreter.Sync (interpretScopedSync, interpretScopedSyncAs, interpretSync, interpretSyncAs)@@ -350,15 +274,11 @@ -- -- When the first thunk finishes, the other will be killed. --- $signal--- #signal#- -- $mask -- #mask#--- The two effects 'Mask' and 'UninterruptibleMask' use the 'Polysemy.Scoped' pattern, which allow an effect--- with resources to be constrained to a region of code.--- The actual effect is 'Polysemy.Conc.Effect.Mask.RestoreMask', with `mask` and 'uninterruptibleMask' only specializing--- 'Polysemy.Conc.Effect.scoped' to the appropriate resource type.+-- The 'Mask' effect uses the 'Polysemy.Scoped' pattern with 'MaskMode' as the scope parameter.+-- 'mask' and 'uninterruptibleMask' are convenience functions that pass 'Interruptible' or 'Uninterruptible' to+-- 'Polysemy.scoped'. -- -- Usage is straightforward: --@@ -371,5 +291,3 @@ -- doUnmaskedThing -- doMaskedThing -- @------ The @resource@ parameter stays polymorphic; it is used to connect the resource in the interpreter to the callsite.
lib/Polysemy/Conc/Async.hs view
@@ -1,4 +1,4 @@--- |Description: Async Combinators+-- | Description: Async Combinators module Polysemy.Conc.Async where import qualified Control.Concurrent.Async as Base@@ -12,7 +12,7 @@ import qualified Polysemy.Conc.Race as Race import Polysemy.Conc.Sync (withSync) --- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- | Run the first action asynchronously while the second action executes, then cancel the first action. -- Passes the handle into the action to allow it to await its result. -- -- When cancelling, this variant will wait indefinitely for the thread to be gone.@@ -25,7 +25,7 @@ handle <- async mb finally (use handle) (cancel handle) --- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- | Run the first action asynchronously while the second action executes, then cancel the first action. -- Passes the handle into the sync action to allow it to await the async action's result. -- -- When cancelling, this variant will wait for the specified interval for the thread to be gone.@@ -40,7 +40,7 @@ handle <- async mb finally (use handle) (Race.timeoutU interval (cancel handle)) --- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- | Run the first action asynchronously while the second action executes, then cancel the first action. -- Passes the handle into the sync action to allow it to await the async action's result. -- -- When cancelling, this variant will wait for 500ms for the thread to be gone.@@ -52,7 +52,7 @@ withAsync = withAsyncWait (MilliSeconds 500) --- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- | Run the first action asynchronously while the second action executes, then cancel the first action. -- Discards the handle, expecting the async action to either terminate or be cancelled. -- -- When cancelling, this variant will wait for 500ms for the thread to be gone.@@ -64,7 +64,7 @@ withAsync_ mb = withAsync mb . const --- |Run an action with 'async', but don't start it right away, so the thread handle can be processed before the action+-- | Run an action with 'async', but don't start it right away, so the thread handle can be processed before the action -- executes. -- -- Takes a callback function that is invoked after spawning the thread.@@ -93,7 +93,7 @@ raise mb f h (Sync.putBlock ()) --- |Variant of 'scheduleAsync' that directly interprets the 'Control.Concurrent.MVar' used for signalling.+-- | Variant of 'scheduleAsync' that directly interprets the 'Control.Concurrent.MVar' used for signalling. scheduleAsyncIO :: ∀ b r a . Members [Resource, Async, Race, Embed IO] r =>@@ -107,7 +107,7 @@ raise mb f h (Sync.putBlock ()) --- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- | Run the first action asynchronously while the second action executes, then cancel the first action. -- -- The second action will only start when the first action calls 'Polysemy.Conc.Gate.signal'. --@@ -125,7 +125,7 @@ gate raise (use h) --- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- | Run the first action asynchronously while the second action executes, then cancel the first action. -- -- The second action will only start when the first action calls 'Polysemy.Conc.Gate.signal'. --
lib/Polysemy/Conc/AtomicState.hs view
@@ -1,9 +1,9 @@--- |Description: AtomicState Interpreters+-- | Description: AtomicState Interpreters module Polysemy.Conc.AtomicState where import Control.Concurrent.STM (newTVarIO) --- |Convenience wrapper around 'runAtomicStateTVar' that creates a new 'Control.Concurrent.STM.TVar'.+-- | Convenience wrapper around 'runAtomicStateTVar' that creates a new 'Control.Concurrent.STM.TVar'. interpretAtomic :: ∀ a r . Member (Embed IO) r =>
lib/Polysemy/Conc/Data/QueueResult.hs view
@@ -1,7 +1,7 @@--- |Description: Queue result ADT+-- | Description: Queue result ADT module Polysemy.Conc.Data.QueueResult where --- |Encodes failure reasons for queues.+-- | Encodes failure reasons for queues. -- -- For documentation on the constructors, see the module "Polysemy.Conc.Data.QueueResult". --@@ -9,13 +9,13 @@ -- import qualified Polysemy.Conc.Data.QueueResult as QueueResult -- @ data QueueResult d =- -- |The operation was successful.+ -- | The operation was successful. Success d |- -- |The queue is either full and cannot be added to, or empty and cannot be read from.+ -- | The queue is either full and cannot be added to, or empty and cannot be read from. NotAvailable |- -- |The queue was closed by the user.+ -- | The queue was closed by the user. Closed deriving stock (Eq, Show, Ord, Functor, Generic)
lib/Polysemy/Conc/Effect/Critical.hs view
@@ -1,21 +1,21 @@ {-# options_haddock prune #-} --- |Description: Critical effect+-- | Description: Critical effect module Polysemy.Conc.Effect.Critical where import Prelude hiding (catch) --- |An effect that catches exceptions.+-- | An effect that catches exceptions. -- -- Provides the exact functionality of `Polysemy.Error.fromExceptionSem`, but pushes the dependency on @Final IO@ to the -- interpreter, and makes it optional. data Critical :: Effect where- -- |Catch all exceptions of type @e@ in this computation.+ -- | Catch all exceptions of type @e@ in this computation. Catch :: Exception e => m a -> Critical m (Either e a) makeSem ''Critical --- |Catch exceptions of type @e@ and return a fallback value.+-- | Catch exceptions of type @e@ and return a fallback value. catchAs :: ∀ e a r . Exception e =>@@ -26,7 +26,7 @@ catchAs a = fmap (fromRight a) . catch @_ @e --- |Convenience overload for 'SomeException'.+-- | Convenience overload for 'SomeException'. run :: Member Critical r => Sem r a ->@@ -34,7 +34,7 @@ run = catch --- |Convenience overload for 'SomeException'.+-- | Convenience overload for 'SomeException'. runAs :: Member Critical r => a ->
lib/Polysemy/Conc/Effect/Events.hs view
@@ -1,34 +1,34 @@ {-# options_haddock prune #-} --- |Description: Events/Consume Effects, Internal+-- | Description: Events/Consume Effects, Internal module Polysemy.Conc.Effect.Events where --- |An event publisher that can be consumed from multiple threads.+-- | An event publisher that can be consumed from multiple threads. data Events (e :: Type) :: Effect where Publish :: e -> Events e m () makeSem_ ''Events --- |Publish one event.+-- | Publish one event. publish :: ∀ e r . Member (Events e) r => e -> Sem r () --- |Consume events emitted by 'Events'.+-- | Consume events emitted by 'Events'. data Consume (e :: Type) :: Effect where Consume :: Consume e m e makeSem_ ''Consume --- |Consume one event emitted by 'Events'.+-- | Consume one event emitted by 'Events'. consume :: ∀ e r . Member (Consume e) r => Sem r e --- |Create a new scope for 'Events', causing the nested program to get its own copy of the event stream.+-- | Create a new scope for 'Events', causing the nested program to get its own copy of the event stream. -- To be used with 'Polysemy.Conc.interpretEventsChan'. subscribe :: ∀ e r .
lib/Polysemy/Conc/Effect/Gate.hs view
@@ -1,9 +1,9 @@ {-# options_haddock prune #-} --- |Description: Gate effect, Internal+-- | Description: Gate effect, Internal module Polysemy.Conc.Effect.Gate where --- |A single-use synchronization point that blocks all consumers who called 'gate' until 'signal' is called.+-- | A single-use synchronization point that blocks all consumers who called 'gate' until 'signal' is called. -- -- The constructors are exported from [Polysemy.Conc.Gate]("Polysemy.Conc.Gate"). data Gate :: Effect where@@ -12,11 +12,11 @@ makeSem ''Gate --- |Convenience alias for scoped 'Gate'.+-- | Convenience alias for scoped 'Gate'. type Gates = Scoped_ Gate --- |Run an action with a locally scoped 'Gate' effect.+-- | Run an action with a locally scoped 'Gate' effect. -- -- This avoids a dependency on @'Embed' 'IO'@ in application logic while still allowing the effect to be scoped. withGate ::
− lib/Polysemy/Conc/Effect/Interrupt.hs
@@ -1,44 +0,0 @@-{-# options_haddock prune #-}--- |Description: Interrupt effect-module Polysemy.Conc.Effect.Interrupt where---- |The interrupt handler effect allows three kinds of interaction for interrupt signals:------ - Execute a callback when a signal is received--- - Block a thread until a signal is received--- - Kill a thread when a signal is received------ For documentation on the constructors, see the module "Polysemy.Conc.Effect.Interrupt".------ @--- import qualified Polysemy.Conc.Effect.Interrupt as Interrupt------ prog = do--- Interrupt.register "task 1" (putStrLn "interrupted")--- Interrupt.killOnQuit $ forever do--- doSomeWork--- @-data Interrupt :: Effect where- -- |Add a computation to be executed on interrupt, using the first argument as a key.- Register :: Text -> IO () -> Interrupt m ()- -- |Remove the previously added handler with the given key.- Unregister :: Text -> Interrupt m ()- -- |Manually trigger the interrupt.- Quit :: Interrupt m ()- -- |Block until an interrupt is triggered.- WaitQuit :: Interrupt m ()- -- |Indicate whether an interrupt was triggered.- Interrupted :: Interrupt m Bool- -- |Execute a computation, waiting for it to finish, killing its thread on interrupt.- KillOnQuit :: Text -> m a -> Interrupt m (Maybe a)--makeSem ''Interrupt---- |Variant of 'killOnQuit' that returns @()@.-killOnQuit_ ::- Member Interrupt r =>- Text ->- Sem r a ->- Sem r ()-killOnQuit_ desc ma =- void (killOnQuit desc ma)
lib/Polysemy/Conc/Effect/Lock.hs view
@@ -1,23 +1,23 @@--- |Lock effect, Internal+-- | Lock effect, Internal module Polysemy.Conc.Effect.Lock where --- |An exclusive lock or mutex, protecting a region from concurrent access.+-- | An exclusive lock or mutex, protecting a region from concurrent access. data Lock :: Effect where- -- |Run an action if the lock is available, block otherwise.+ -- | Run an action if the lock is available, block otherwise. Lock :: m a -> Lock m a- -- |Run the second action if the lock is available, or the first action otherwise.+ -- | Run the second action if the lock is available, or the first action otherwise. LockOr :: m a -> m a -> Lock m a makeSem_ ''Lock --- |Run an action if the lock is available, block otherwise.+-- | Run an action if the lock is available, block otherwise. lock :: ∀ r a . Member Lock r => Sem r a -> Sem r a --- |Run an action if the lock is available, block otherwise.+-- | Run an action if the lock is available, block otherwise. lockOr :: ∀ r a . Member Lock r =>@@ -25,7 +25,7 @@ Sem r a -> Sem r a --- |Run an action if the lock is available, skip and return 'Nothing' otherwise.+-- | Run an action if the lock is available, skip and return 'Nothing' otherwise. lockOrSkip :: ∀ r a . Member Lock r =>@@ -35,7 +35,7 @@ lockOr (pure Nothing) (Just <$> ma) {-# inline lockOrSkip #-} --- |Run an action if the lock is available, skip otherwise.+-- | Run an action if the lock is available, skip otherwise. -- Return @()@. lockOrSkip_ :: ∀ r a .
lib/Polysemy/Conc/Effect/Mask.hs view
@@ -1,15 +1,15 @@ {-# options_haddock prune #-} --- |Description: Mask Effect, Internal+-- | Description: Mask Effect, Internal module Polysemy.Conc.Effect.Mask where --- |Part of an effect abstracting 'Control.Exception.mask'.+-- | Part of an effect abstracting 'Control.Exception.mask'. data RestoreMask :: Effect where Restore :: m a -> RestoreMask m a makeSem_ ''RestoreMask --- |Restore the previous masking state.+-- | Restore the previous masking state. -- Can only be called inside of an action passed to 'mask' or 'uninterruptibleMask'. restore :: ∀ r a .@@ -17,30 +17,37 @@ Sem r a -> Sem r a --- |Resource type for the scoped 'Mask' effect, wrapping the @restore@ callback passed in by 'Base.mask'.+-- | Resource type for the scoped 'Mask' effect, wrapping the @restore@ callback passed in by 'Base.mask'. newtype Restoration = Restoration { unRestoration :: ∀ a . IO a -> IO a } --- |The scoped masking effect.+-- | Whether 'mask' or 'uninterruptibleMask' was requested.+data MaskMode =+ Interruptible+ |+ Uninterruptible+ deriving stock (Eq, Show)++-- | The scoped masking effect, parameterized by 'MaskMode'. type Mask =- Scoped_ RestoreMask+ Scoped MaskMode RestoreMask --- |The scoped uninterruptible masking effect.-type UninterruptibleMask =- Scoped_ RestoreMask+-- | Deprecated synonym for 'Mask'.+type UninterruptibleMask = Mask+{-# deprecated UninterruptibleMask "Use Mask instead, which now handles both variants via MaskMode" #-} --- |Mark a region as masked.--- Uses the 'Scoped_' pattern.+-- | Mark a region as masked.+-- Uses the 'Scoped' pattern with 'Interruptible'. mask :: Member Mask r => InterpreterFor RestoreMask r mask =- scoped_+ scoped Interruptible --- |Mark a region as uninterruptibly masked.--- Uses the 'Scoped_' pattern.+-- | Mark a region as uninterruptibly masked.+-- Uses the 'Scoped' pattern with 'Uninterruptible'. uninterruptibleMask ::- Member UninterruptibleMask r =>+ Member Mask r => InterpreterFor RestoreMask r uninterruptibleMask =- scoped_+ scoped Uninterruptible
lib/Polysemy/Conc/Effect/Monitor.hs view
@@ -1,16 +1,16 @@ {-# options_haddock prune #-} --- |Description: Monitor Effect, Internal+-- | Description: Monitor Effect, Internal module Polysemy.Conc.Effect.Monitor where import Polysemy.Time (NanoSeconds) --- |Marker type for the restarting action for 'Monitor'.+-- | Marker type for the restarting action for 'Monitor'. data Restart = Restart deriving stock (Eq, Show) --- |Mark a region as being subject to intervention by a monitoring program.+-- | Mark a region as being subject to intervention by a monitoring program. -- This can mean that a thread is repeatedly checking a condition and cancelling this region when it is unmet. -- A use case could be checking whether a remote service is available, or whether the system was suspended and resumed. -- This should be used in a 'Scoped_' context, like 'withMonitor'.@@ -19,26 +19,26 @@ makeSem_ ''Monitor --- |Mark a region as being subject to intervention by a monitoring program.+-- | Mark a region as being subject to intervention by a monitoring program. monitor :: ∀ action r a . Member (Monitor action) r => Sem r a -> Sem r a --- |Convenience alias for a 'Scoped_' 'Monitor'.+-- | Convenience alias for a 'Scoped_' 'Monitor'. type ScopedMonitor (action :: Type) = Scoped_ (Monitor action) --- |'Monitor' specialized to the 'Restart' action.+-- | 'Monitor' specialized to the 'Restart' action. type RestartingMonitor = ScopedMonitor Restart --- |Resources for a 'Scoped_' 'Monitor'.+-- | Resources for a 'Scoped_' 'Monitor'. data MonitorCheck r = MonitorCheck { interval :: NanoSeconds,- check :: MVar () -> Sem r ()+ check :: Sem r Bool } -- | Transform the stack of the check in a 'MonitorCheck'.@@ -47,9 +47,9 @@ MonitorCheck r -> MonitorCheck r' hoistMonitorCheck f MonitorCheck {..} =- MonitorCheck {check = f . check, ..}+ MonitorCheck {check = f check, ..} --- |Start a region that can contain monitor-intervention regions.+-- | Start a region that can contain monitor-intervention regions. withMonitor :: ∀ action r . Member (ScopedMonitor action) r =>@@ -57,7 +57,7 @@ withMonitor = scoped_ --- |Variant of 'withMonitor' that uses the 'Restart' strategy.+-- | Variant of 'withMonitor' that uses the 'Restart' strategy. restart :: Member (ScopedMonitor Restart) r => InterpreterFor (Monitor Restart) r
lib/Polysemy/Conc/Effect/Queue.hs view
@@ -1,11 +1,11 @@ {-# options_haddock prune #-}--- |Description: Queue effect+-- | Description: Queue effect module Polysemy.Conc.Effect.Queue where import Polysemy.Time (TimeUnit) import Polysemy.Conc.Data.QueueResult (QueueResult) --- |Abstracts queues like 'Control.Concurrent.STM.TBQueue'.+-- | Abstracts queues like 'Control.Concurrent.STM.TBQueue'. -- -- For documentation on the constructors, see the module "Polysemy.Conc.Data.Queue". --@@ -22,25 +22,25 @@ -- r -> pure r -- @ data Queue d :: Effect where- -- |Read an element from the queue, blocking until one is available.+ -- | Read an element from the queue, blocking until one is available. Read :: Queue d m (QueueResult d)- -- |Read an element from the queue, immediately returning if none is available.+ -- | Read an element from the queue, immediately returning if none is available. TryRead :: Queue d m (QueueResult d)- -- |Read an element from the queue, blocking until one is available or the timeout expires.+ -- | Read an element from the queue, blocking until one is available or the timeout expires. ReadTimeout :: TimeUnit t => t -> Queue d m (QueueResult d)- -- |Read an element, leaving it in the queue, blocking until one is available.+ -- | Read an element, leaving it in the queue, blocking until one is available. Peek :: Queue d m (QueueResult d)- -- |Read an element, leaving it in the queue, immediately returning if none is available.+ -- | Read an element, leaving it in the queue, immediately returning if none is available. TryPeek :: Queue d m (QueueResult d)- -- |Write an element to the queue, blocking until a slot is available.+ -- | Write an element to the queue, blocking until a slot is available. Write :: d -> Queue d m ()- -- |Write an element to the queue, immediately returning if no slot is available.+ -- | Write an element to the queue, immediately returning if no slot is available. TryWrite :: d -> Queue d m (QueueResult ())- -- |Write an element to the queue, blocking until a slot is available or the timeout expires.+ -- | Write an element to the queue, blocking until a slot is available or the timeout expires. WriteTimeout :: TimeUnit t => t -> d -> Queue d m (QueueResult ())- -- |Indicate whether the queue is closed.+ -- | Indicate whether the queue is closed. Closed :: Queue d m Bool- -- |Close the queue.+ -- | Close the queue. Close :: Queue d m () makeSem ''Queue
lib/Polysemy/Conc/Effect/Race.hs view
@@ -1,21 +1,21 @@ {-# options_haddock prune #-} --- |Description: Race effect+-- | Description: Race effect module Polysemy.Conc.Effect.Race where import Polysemy.Time (TimeUnit) --- |Abstract the concept of running two programs concurrently, aborting the other when one terminates.+-- | Abstract the concept of running two programs concurrently, aborting the other when one terminates. -- 'Timeout' is a simpler variant, where one thread just sleeps for a given interval. data Race :: Effect where- -- |Run both programs concurrently, returning the result of the faster one.+ -- | Run both programs concurrently, returning the result of the faster one. Race :: m a -> m b -> Race m (Either a b)- -- |Run the first action if the second action doesn't finish within the specified interval.+ -- | Run the first action if the second action doesn't finish within the specified interval. Timeout :: TimeUnit u => m a -> u -> m b -> Race m (Either a b) makeSem_ ''Race --- |Run both programs concurrently, returning the result of the faster one.+-- | Run both programs concurrently, returning the result of the faster one. race :: ∀ a b r . Member Race r =>@@ -23,7 +23,7 @@ Sem r b -> Sem r (Either a b) --- |Run the fallback action if the given program doesn't finish within the specified interval.+-- | Run the fallback action if the given program doesn't finish within the specified interval. timeout :: ∀ a b u r . TimeUnit u =>
− lib/Polysemy/Conc/Effect/Scoped.hs
@@ -1,155 +0,0 @@-{-# options_haddock prune #-}---- |Description: Scoped Effect, Internal-module Polysemy.Conc.Effect.Scoped where--import Prelude hiding (Scoped, Scoped_, scoped)---- | @Scoped@ transforms a program so that an interpreter for @effect@ may--- perform arbitrary actions, like resource management, before and after the--- computation wrapped by a call to 'scoped' is executed.------ /Note/: This effect has been merged to Polysemy and will be released there soon.------ An application for this is @Polysemy.Conc.Events@ from--- <https://hackage.haskell.org/package/polysemy-conc>, in which each program--- using the effect @Polysemy.Conc.Consume@ is interpreted with its own copy of--- the event channel; or a database transaction, in which a transaction handle--- is created for the wrapped program and passed to the interpreter for the--- database effect.------ For a longer exposition, see <https://www.tweag.io/blog/2022-01-05-polysemy-scoped/>.--- Note that the interface has changed since the blog post was published: The--- @resource@ parameter no longer exists.------ Resource allocation is performed by a function passed to--- 'Polysemy.Scoped.interpretScoped'.------ The constructors are not intended to be used directly; the smart constructor--- 'scoped' is used like a local interpreter for @effect@. 'scoped' takes an--- argument of type @param@, which will be passed through to the interpreter, to--- be used by the resource allocation function.------ As an example, imagine an effect for writing lines to a file:------ > data Write :: Effect where--- > Write :: Text -> Write m ()--- > makeSem ''Write------ If we now have the following requirements:------ 1. The file should be opened and closed right before and after the part of--- the program in which we write lines--- 2. The file name should be specifiable at the point in the program where--- writing begins--- 3. We don't want to commit to IO, lines should be stored in memory when--- running tests------ Then we can take advantage of 'Scoped' to write this program:------ > prog :: Member (Scoped FilePath Write) r => Sem r ()--- > prog = do--- > scoped "file1.txt" do--- > write "line 1"--- > write "line 2"--- > scoped "file2.txt" do--- > write "line 1"--- > write "line 2"------ Here 'scoped' creates a prompt for an interpreter to start allocating a--- resource for @"file1.txt"@ and handling @Write@ actions using that resource.--- When the 'scoped' block ends, the resource should be freed.------ The interpreter may look like this:------ > interpretWriteFile :: Members '[Resource, Embed IO] => InterpreterFor (Scoped FilePath Write) r--- > interpretWriteFile =--- > interpretScoped allocator handler--- > where--- > allocator name use = bracket (openFile name WriteMode) hClose use--- > handler fileHandle (Write line) = embed (Text.hPutStrLn fileHandle line)------ Essentially, the @bracket@ is executed at the point where @scoped@ was--- called, wrapping the following block. When the second @scoped@ is executed,--- another call to @bracket@ is performed.------ The effect of this is that the operation that uses @Embed IO@ was moved from--- the call site to the interpreter, while the interpreter may be executed at--- the outermost layer of the app.------ This makes it possible to use a pure interpreter for testing:------ > interpretWriteOutput :: Member (Output (FilePath, Text)) r => InterpreterFor (Scoped FilePath Write) r--- > interpretWriteOutput =--- > interpretScoped (\ name use -> use name) \ name -> \case--- > Write line -> output (name, line)------ Here we simply pass the name to the interpreter in the resource allocation--- function.------ Now imagine that we drop requirement 2 from the initial list – we still want--- the file to be opened and closed as late/early as possible, but the file name--- is globally fixed. For this case, the @param@ type is unused, and the API--- provides some convenience aliases to make your code more concise:------ > prog :: Member (Scoped_ Write) r => Sem r ()--- > prog = do--- > scoped_ do--- > write "line 1"--- > write "line 2"--- > scoped_ do--- > write "line 1"--- > write "line 2"------ The type 'Scoped_' and the constructor 'scoped_' simply fix @param@ to @()@.-data Scoped (param :: Type) (effect :: Effect) :: Effect where- Run :: ∀ param effect m a . effect m a -> Scoped param effect m a- InScope :: ∀ param effect m a . param -> m a -> Scoped param effect m a--{-# deprecated Scoped "Scoped has been moved to Polysemy.Scoped" #-}---- |A convenience alias for a scope without parameters.-type Scoped_ effect =- Scoped () effect---- | Constructor for 'Scoped', taking a nested program and transforming all--- instances of @effect@ to @'Scoped' param effect@.------ Please consult the documentation of 'Scoped' for details and examples.-scoped ::- ∀ param effect r .- Member (Scoped param effect) r =>- param ->- InterpreterFor effect r-scoped param main =- send $ InScope @param @effect param do- transform @effect (Run @param) main-{-# inline scoped #-}---- | Constructor for 'Scoped_', taking a nested program and transforming all--- instances of @effect@ to @'Scoped_' effect@.------ Please consult the documentation of 'Scoped' for details and examples.-scoped_ ::- ∀ effect r .- Member (Scoped_ effect) r =>- InterpreterFor effect r-scoped_ = scoped ()-{-# inline scoped_ #-}---- | Transform the parameters of a 'Scoped' program.------ This allows incremental additions to the data passed to the interpreter, for--- example to create an API that permits different ways of running an effect--- with some fundamental parameters being supplied at scope creation and some--- optional or specific parameters being selected by the user downstream.-rescope ::- ∀ param0 param1 effect r .- Member (Scoped param1 effect) r =>- (param0 -> param1) ->- InterpreterFor (Scoped param0 effect) r-rescope fp =- transform \case- Run e -> Run @param1 e- InScope p main -> InScope (fp p) main-{-# inline rescope #-}
lib/Polysemy/Conc/Effect/Semaphore.hs view
@@ -1,23 +1,23 @@--- |Semaphore effect, Internal.+-- | Semaphore effect, Internal. module Polysemy.Conc.Effect.Semaphore where --- |This effect abstracts over the concept of a quantity semaphore, a concurrency primitive that contains a number of+-- | This effect abstracts over the concept of a quantity semaphore, a concurrency primitive that contains a number of -- slots that can be acquired and released. data Semaphore :: Effect where- -- |Wait until a slot is available, then acquire it.+ -- | Wait until a slot is available, then acquire it. Wait :: Semaphore m ()- -- |Release a slot.+ -- | Release a slot. Signal :: Semaphore m () makeSem_ ''Semaphore --- |Wait until a slot is available, then acquire it.+-- | Wait until a slot is available, then acquire it. wait :: ∀ r . Member Semaphore r => Sem r () --- |Release a slot.+-- | Release a slot. signal :: ∀ r . Member Semaphore r =>
lib/Polysemy/Conc/Effect/Sync.hs view
@@ -1,12 +1,12 @@ {-# options_haddock prune #-} --- |Description: Sync effect, Internal.+-- | Description: Sync effect, Internal. module Polysemy.Conc.Effect.Sync where import Polysemy.Time (TimeUnit) import Prelude hiding (empty, try) --- |Abstracts an 'Control.Concurrent.MVar'.+-- | Abstracts an 'Control.Concurrent.MVar'. -- -- For documentation on the constructors, see the module "Polysemy.Conc.Effect.Sync". --@@ -20,29 +20,29 @@ -- Sync.takeBlock -- @ data Sync d :: Effect where- -- |Read the variable, waiting until a value is available.+ -- | Read the variable, waiting until a value is available. Block :: Sync d m d- -- |Read the variable, waiting until a value is available or the timeout has expired.+ -- | Read the variable, waiting until a value is available or the timeout has expired. Wait :: TimeUnit u => u -> Sync d m (Maybe d)- -- |Read the variable, returning 'Nothing' immmediately if no value was available.+ -- | Read the variable, returning 'Nothing' immmediately if no value was available. Try :: Sync d m (Maybe d)- -- |Take the variable, waiting until a value is available.+ -- | Take the variable, waiting until a value is available. TakeBlock :: Sync d m d- -- |Take the variable, waiting until a value is available or the timeout has expired.+ -- | Take the variable, waiting until a value is available or the timeout has expired. TakeWait :: TimeUnit u => u -> Sync d m (Maybe d)- -- |Take the variable, returning 'Nothing' immmediately if no value was available.+ -- | Take the variable, returning 'Nothing' immmediately if no value was available. TakeTry :: Sync d m (Maybe d)- -- |Write the variable, waiting until it is writable.+ -- | Write the variable, waiting until it is writable. PutBlock :: d -> Sync d m ()- -- |Write the variable, waiting until it is writable or the timeout has expired.+ -- | Write the variable, waiting until it is writable or the timeout has expired. PutWait :: TimeUnit u => u -> d -> Sync d m Bool- -- |Write the variable, returning 'False' immmediately if a value was available.+ -- | Write the variable, returning 'False' immmediately if a value was available. PutTry :: d -> Sync d m Bool- -- |Indicate whether the variable is empty.+ -- | Indicate whether the variable is empty. Empty :: Sync d m Bool makeSem ''Sync --- |Convenience alias.+-- | Convenience alias. type ScopedSync a = Scoped_ (Sync a)
lib/Polysemy/Conc/Effect/SyncRead.hs view
@@ -1,20 +1,20 @@ {-# options_haddock prune #-} --- |The effect 'SyncRead' is equivalent to 'Polysemy.Conc.Sync' without the write actions.+-- | The effect 'SyncRead' is equivalent to 'Polysemy.Conc.Sync' without the write actions. module Polysemy.Conc.Effect.SyncRead where import Polysemy.Time (TimeUnit) import Prelude hiding (empty, try) --- |An interface to a shared variable ('MVar') that can only be read.+-- | An interface to a shared variable ('MVar') that can only be read. data SyncRead (d :: Type) :: Effect where- -- |Read the variable, waiting until a value is available.+ -- | Read the variable, waiting until a value is available. Block :: SyncRead d m d- -- |Read the variable, waiting until a value is available or the timeout has expired.+ -- | Read the variable, waiting until a value is available or the timeout has expired. Wait :: TimeUnit u => u -> SyncRead d m (Maybe d)- -- |Read the variable, returning 'Nothing' immmediately if no value was available.+ -- | Read the variable, returning 'Nothing' immmediately if no value was available. Try :: SyncRead d m (Maybe d)- -- |Indicate whether the variable is empty.+ -- | Indicate whether the variable is empty. Empty :: SyncRead d m Bool makeSem ''SyncRead
lib/Polysemy/Conc/Events.hs view
@@ -1,4 +1,4 @@--- |Description: Events Combinators+-- | Description: Events Combinators module Polysemy.Conc.Events where import Polysemy.Conc.Async (withAsync_)@@ -8,7 +8,7 @@ import Polysemy.Conc.Effect.Race (Race) import Polysemy.Conc.Interpreter.Events (EventConsumer) --- |Create a new scope for 'Polysemy.Conc.Events', causing the nested program to get its own copy of the event stream.+-- | Create a new scope for 'Polysemy.Conc.Events', causing the nested program to get its own copy of the event stream. -- -- Calls 'signal' before running the argument to ensure that 'Events.subscribe' has finished creating a channel, for use -- with asynchronous execution.@@ -21,7 +21,7 @@ signal action --- |Create a new scope for 'Polysemy.Conc.Events', causing the nested program to get its own copy of the event stream.+-- | Create a new scope for 'Polysemy.Conc.Events', causing the nested program to get its own copy of the event stream. -- -- Executes in a new thread, ensuring that the main thread blocks until 'Events.subscribe' has finished creating a -- channel.@@ -36,7 +36,7 @@ gate raise @Gate ma --- |Pull repeatedly from 'Polysemy.Conc.Consume', passing the event to the supplied callback.+-- | Pull repeatedly from 'Polysemy.Conc.Consume', passing the event to the supplied callback. -- Stop when the action returns @False@. consumeWhile :: Member (Consume e) r =>@@ -48,7 +48,7 @@ spin = whenM (action =<< Events.consume) spin --- |Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback.+-- | Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback. -- Stop when the action returns @False@. subscribeWhile :: ∀ e r .@@ -58,7 +58,7 @@ subscribeWhile action = Events.subscribe @e (consumeWhile (raise . action)) --- |Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback.+-- | Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback. -- Stop when the action returns @False@. -- -- Signals the caller that the channel was successfully subscribed to using the 'Gate' effect.@@ -71,7 +71,7 @@ subscribeGated @e do consumeWhile (raise . action) --- |Start a new thread that pulls repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied+-- | Start a new thread that pulls repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied -- callback and stops when the action returns @False@. subscribeWhileAsync :: ∀ e r a .@@ -82,7 +82,7 @@ subscribeWhileAsync action = subscribeAsync @e (consumeWhile action) --- |Pull repeatedly from 'Polysemy.Conc.Consume', passing the event to the supplied callback.+-- | Pull repeatedly from 'Polysemy.Conc.Consume', passing the event to the supplied callback. consumeLoop :: Member (Consume e) r => (e -> Sem r ()) ->@@ -90,7 +90,7 @@ consumeLoop action = forever (action =<< Events.consume) --- |Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback.+-- | Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback. subscribeLoop :: ∀ e r . Member (EventConsumer e) r =>@@ -99,7 +99,7 @@ subscribeLoop action = Events.subscribe @e (consumeLoop (raise . action)) --- |Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback.+-- | Pull repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied callback. -- -- Signals the caller that the channel was successfully subscribed to using the 'Gate' effect. subscribeLoopGated ::@@ -111,7 +111,7 @@ subscribeGated @e do consumeLoop (raise . action) --- |Start a new thread that pulls repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied+-- | Start a new thread that pulls repeatedly from the 'Polysemy.Conc.Events' channel, passing the event to the supplied -- callback. subscribeLoopAsync :: ∀ e r a .@@ -122,7 +122,7 @@ subscribeLoopAsync action = subscribeAsync @e (consumeLoop action) --- |Block until a value matching the predicate has been returned by 'Polysemy.Conc.Consume'.+-- | Block until a value matching the predicate has been returned by 'Polysemy.Conc.Consume'. consumeFind :: ∀ e r . Member (Consume e) r =>@@ -135,7 +135,7 @@ e <- Events.consume ifM (f e) (pure e) spin --- |Block until a value matching the predicate has been published to the 'Polysemy.Conc.Events' channel.+-- | Block until a value matching the predicate has been published to the 'Polysemy.Conc.Events' channel. subscribeFind :: ∀ e r . Member (EventConsumer e) r =>@@ -144,7 +144,7 @@ subscribeFind f = Events.subscribe @e (consumeFind f) --- |Return the first value returned by 'Polysemy.Conc.Consume' for which the function produces 'Just'.+-- | Return the first value returned by 'Polysemy.Conc.Consume' for which the function produces 'Just'. consumeFirstJust :: ∀ e a r . Member (Consume e) r =>@@ -157,7 +157,7 @@ e <- Events.consume maybe spin pure =<< f e --- |Return the first value published to the 'Polysemy.Conc.Events' channel for which the function produces 'Just'.+-- | Return the first value published to the 'Polysemy.Conc.Events' channel for which the function produces 'Just'. subscribeFirstJust :: ∀ e a r . Member (EventConsumer e) r =>@@ -166,7 +166,7 @@ subscribeFirstJust f = Events.subscribe @e (consumeFirstJust f) --- |Block until the specified value has been returned by 'Polysemy.Conc.Consume'.+-- | Block until the specified value has been returned by 'Polysemy.Conc.Consume'. consumeElem :: ∀ e r . Eq e =>@@ -176,7 +176,7 @@ consumeElem target = void (consumeFind (pure . (target ==))) --- |Block until the specified value has been published to the 'Polysemy.Conc.Events' channel.+-- | Block until the specified value has been published to the 'Polysemy.Conc.Events' channel. subscribeElem :: ∀ e r . Eq e =>
lib/Polysemy/Conc/Gate.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: API for the 'Gate' effect+-- | Description: API for the 'Gate' effect module Polysemy.Conc.Gate ( module Polysemy.Conc.Effect.Gate, module Polysemy.Conc.Interpreter.Gate,
lib/Polysemy/Conc/Interpreter/Critical.hs view
@@ -1,4 +1,4 @@--- |Description: Critical interpreters+-- | Description: Critical interpreters module Polysemy.Conc.Interpreter.Critical where import qualified Control.Exception as Exception@@ -6,7 +6,7 @@ import Polysemy.Conc.Effect.Critical (Critical (..)) --- |Interpret 'Critical' in terms of 'Final' 'IO'.+-- | Interpret 'Critical' in terms of 'Final' 'IO'. interpretCritical :: Member (Final IO) r => InterpreterFor Critical r@@ -21,7 +21,7 @@ Exception.catch (fmap Right <$> ma') \ se -> pure (Left se <$ s) {-# inline interpretCritical #-} --- |Interpret 'Critical' by doing nothing.+-- | Interpret 'Critical' by doing nothing. interpretCriticalNull :: InterpreterFor Critical r interpretCriticalNull =
lib/Polysemy/Conc/Interpreter/Events.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Events/Consume Interpreters, Internal+-- | Description: Events/Consume Interpreters, Internal module Polysemy.Conc.Interpreter.Events where import Control.Concurrent.Chan.Unagi.Bounded (InChan, OutChan, dupChan, newChan, readChan, tryWriteChan)@@ -10,11 +10,11 @@ import Polysemy.Conc.Effect.Events (Consume, Events) import Polysemy.Conc.Effect.Race (Race) --- |Convenience alias for the consumer effect.+-- | Convenience alias for the consumer effect. type EventConsumer e = Scoped_ (Consume e) --- |Interpret 'Consume' by reading from an 'OutChan'.+-- | Interpret 'Consume' by reading from an 'OutChan'. -- Used internally by 'interpretEventsChan', not safe to use directly. interpretConsumeChan :: ∀ e r .@@ -26,7 +26,7 @@ Events.Consume -> embed (readChan chan) --- |Interpret 'Events' by writing to an 'InChan'.+-- | Interpret 'Events' by writing to an 'InChan'. -- Used internally by 'interpretEventsChan', not safe to use directly. -- When the channel queue is full, this silently discards events. interpretEventsInChan ::@@ -39,7 +39,7 @@ Events.Publish e -> void (embed (tryWriteChan inChan e)) --- |Interpret 'Events' and 'Consume' together by connecting them to the two ends of an unagi channel.+-- | Interpret 'Events' and 'Consume' together by connecting them to the two ends of an unagi channel. -- 'Consume' is only interpreted in a 'Polysemy.Conc.Scoped' manner, ensuring that a new duplicate of the channel is -- created so that all consumers see all events (from the moment they are connected). --
lib/Polysemy/Conc/Interpreter/Gate.hs view
@@ -1,9 +1,9 @@--- |Gate interpreters, Internal+-- | Gate interpreters, Internal module Polysemy.Conc.Interpreter.Gate where import Polysemy.Conc.Effect.Gate (Gate (Gate, Signal)) --- |Interpret 'Gate' with an 'MVar'.+-- | Interpret 'Gate' with an 'MVar'. interpretGate :: ∀ r . Member (Embed IO) r =>@@ -20,7 +20,7 @@ Gate -> embed (readMVar mv) --- |Interpret @'Scoped_' 'Gate'@ with an @'MVar' ()@.+-- | Interpret @'Scoped_' 'Gate'@ with an @'MVar' ()@. interpretGates :: Member (Embed IO) r => InterpreterFor (Scoped_ Gate) r
− lib/Polysemy/Conc/Interpreter/Interrupt.hs
@@ -1,243 +0,0 @@-{-# options_haddock prune #-}---- |Description: Interrupt interpreters-module Polysemy.Conc.Interpreter.Interrupt where--import qualified Control.Concurrent.Async as A-import Control.Concurrent.Async (AsyncCancelled)-import Control.Concurrent.STM (TVar, newTVarIO)-import qualified Data.Map.Strict as Map-import qualified Data.Set as Set-import qualified Data.Text.IO as Text-import Polysemy.Internal.Tactics (liftT)-import Polysemy.Time (Seconds (Seconds))-import System.IO (stderr)-import System.Posix.Signals (- Handler (Catch, CatchInfo, CatchInfoOnce, CatchOnce),- SignalInfo,- installHandler,- keyboardSignal,- )--import qualified Polysemy.Conc.Effect.Critical as Critical-import Polysemy.Conc.Effect.Critical (Critical)-import Polysemy.Conc.Effect.Interrupt (Interrupt (..))-import Polysemy.Conc.Effect.Race (Race)-import qualified Polysemy.Conc.Effect.Sync as Sync-import Polysemy.Conc.Interpreter.Sync (interpretSync)-import Polysemy.Conc.Race (race_)--putErr ::- Member (Embed IO) r =>- Text ->- Sem r ()-putErr =- embed . Text.hPutStrLn stderr--data InterruptState =- InterruptState {- quit :: !(MVar ()),- finished :: !(MVar ()),- listeners :: !(Set Text),- original :: !(SignalInfo -> IO ()),- handlers :: !(Map Text (IO ()))- }--modListeners :: (Set Text -> Set Text) -> InterruptState -> InterruptState-modListeners f s@InterruptState {listeners} =- s {listeners = f listeners}--modHandlers :: (Map Text (IO ()) -> Map Text (IO ())) -> InterruptState -> InterruptState-modHandlers f s@InterruptState {handlers} =- s {handlers = f handlers}--waitQuit ::- Members [AtomicState InterruptState, Embed IO] r =>- Sem r ()-waitQuit = do- mv <- atomicGets quit- embed (readMVar mv)--checkListeners ::- Members [AtomicState InterruptState, Embed IO] r =>- Sem r ()-checkListeners =- whenM (atomicGets (Set.null . listeners)) do- fin <- atomicGets finished- void (embed (tryPutMVar fin ()))--onQuit ::- Members [AtomicState InterruptState, Embed IO] r =>- Text ->- Sem r a ->- Sem r a-onQuit name ma = do- atomicModify' (modListeners (Set.insert name))- waitQuit- a <- ma- atomicModify' (modListeners (Set.delete name))- checkListeners- pure a--processHandler ::- Member (Embed IO) r =>- Text ->- IO () ->- Sem r ()-processHandler name thunk = do- putErr ("processing interrupt handler: " <> name)- embed thunk--execInterrupt ::- Members [AtomicState InterruptState, Embed IO] r =>- Sem r (SignalInfo -> Sem r ())-execInterrupt = do- InterruptState quitSignal finishSignal _ orig _ <- atomicGet- whenM (embed (tryPutMVar quitSignal ())) do- traverse_ (uncurry processHandler) . Map.toList =<< atomicGets handlers- checkListeners- embed (takeMVar finishSignal)- embed . orig <$ putErr "interrupt handlers finished"--registerHandler ::- Member (AtomicState InterruptState) r =>- Text ->- IO () ->- Sem r ()-registerHandler name handler =- atomicModify' (modHandlers (Map.insert name handler))--awaitOrKill ::- Members [AtomicState InterruptState, Critical, Race, Async, Embed IO] r =>- Text ->- A.Async (Maybe a) ->- Sem r (Maybe a)-awaitOrKill desc handle = do- interpretSync @() do- race_ (catchCritical (await handle)) kill- where- catchCritical =- maybe waitKill (pure . Just) <=< Critical.catchAs @AsyncCancelled Nothing- waitKill =- Nothing <$ Sync.wait @() (Seconds 1)- kill = do- onQuit desc do- putErr ("killing " <> desc)- cancel handle- putErr ("killed " <> desc)- Sync.putBlock ()- pure Nothing--interpretInterruptState ::- Members [AtomicState InterruptState, Critical, Race, Async, Embed IO] r =>- InterpreterFor Interrupt r-interpretInterruptState =- interpretH \case- Register name handler ->- liftT (registerHandler name handler)- Unregister name ->- liftT $ atomicModify' \ s@InterruptState {handlers} -> s {handlers = Map.delete name handlers}- WaitQuit ->- liftT waitQuit- Quit ->- liftT do- putErr "manual interrupt"- void execInterrupt- Interrupted ->- liftT . fmap isJust . embed . tryReadMVar =<< atomicGets quit- KillOnQuit desc ma -> do- maT <- runT ma- ins <- getInspectorT- handle <- raise (interpretInterruptState (async maT))- result <- liftT (awaitOrKill desc handle)- pure (join . fmap (inspect ins) <$> result)-{-# inline interpretInterruptState #-}--broadcastInterrupt ::- Members [AtomicState InterruptState, Embed IO] r =>- SignalInfo ->- Sem r ()-broadcastInterrupt sig = do- putErr "caught interrupt signal"- orig <- execInterrupt- orig sig---- The original handler is either the default handler that kills all threads or a handler installed by an environment--- like ghcid.--- In the latter case, not calling it results in ghcid misbehaving.--- To distinguish the two cases, the constructor used by the default is 'Catch', while a custom handler should usually--- use 'CatchOnce', since you don't want to catch repeated occurences of SIGINT, as it will surely cause problems.-originalHandler :: Handler -> (SignalInfo -> IO ())-originalHandler (CatchOnce thunk) =- (const thunk)-originalHandler (CatchInfoOnce thunk) =- thunk-originalHandler (Catch thunk) =- (const thunk)-originalHandler (CatchInfo thunk) =- thunk-originalHandler _ =- const unit-{-# inline originalHandler #-}--installSignalHandler ::- TVar InterruptState ->- ((SignalInfo -> IO ()) -> Handler) ->- IO Handler-installSignalHandler state consHandler =- installHandler keyboardSignal (consHandler handler) Nothing- where- handler sig =- runFinal $ embedToFinal @IO $ runAtomicStateTVar state (broadcastInterrupt sig)---- |Interpret 'Interrupt' by installing a signal handler.------ Takes a constructor for 'Handler'.-interpretInterruptWith ::- Members [Critical, Race, Async, Embed IO] r =>- ((SignalInfo -> IO ()) -> Handler) ->- InterpreterFor Interrupt r-interpretInterruptWith consHandler sem = do- quitMVar <- embed newEmptyMVar- finishMVar <- embed newEmptyMVar- state <- embed (newTVarIO (InterruptState quitMVar finishMVar Set.empty (const unit) Map.empty))- orig <- embed $ installSignalHandler state consHandler- runAtomicStateTVar state do- atomicModify' \ s -> s {original = originalHandler orig}- interpretInterruptState $ raiseUnder sem---- |Interpret 'Interrupt' by installing a signal handler.------ Catches repeat invocations of SIGINT.-interpretInterrupt ::- Members [Critical, Race, Async, Embed IO] r =>- InterpreterFor Interrupt r-interpretInterrupt =- interpretInterruptWith CatchInfo---- |Interpret 'Interrupt' by installing a signal handler.------ Catches only the first invocation of SIGINT.-interpretInterruptOnce ::- Members [Critical, Race, Async, Embed IO] r =>- InterpreterFor Interrupt r-interpretInterruptOnce =- interpretInterruptWith CatchInfoOnce---- |Eliminate 'Interrupt' without interpreting.-interpretInterruptNull ::- InterpreterFor Interrupt r-interpretInterruptNull =- interpretH \case- Register _ _ ->- pureT ()- Unregister _ ->- pureT ()- WaitQuit ->- pureT ()- Quit ->- pureT ()- Interrupted ->- pureT False- KillOnQuit _ _ ->- pureT Nothing
lib/Polysemy/Conc/Interpreter/Lock.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Lock interpreters, Internal+-- | Lock interpreters, Internal module Polysemy.Conc.Interpreter.Lock where import Control.Concurrent (ThreadId, myThreadId)@@ -18,7 +18,7 @@ currentThread = embed myThreadId --- |Interpret 'Lock' by executing all actions unconditionally.+-- | Interpret 'Lock' by executing all actions unconditionally. interpretLockPermissive :: InterpreterFor Lock r interpretLockPermissive =@@ -84,7 +84,7 @@ restore (raise alt) {-# inline lockAlt #-} --- |Subinterpreter for 'interpretLockReentrant' that checks whether the current thread is equal to the lock-acquiring+-- | Subinterpreter for 'interpretLockReentrant' that checks whether the current thread is equal to the lock-acquiring -- thread to allow reentry into the lock. interpretLockReentrantEntered :: Members [Sync (), Resource, Race, Mask, Embed IO] r =>@@ -99,7 +99,7 @@ lockOnDifferentThread lockThread maI (lockAlt alt) {-# inline interpretLockReentrantEntered #-} --- |Interpret 'Lock' as a reentrant lock, allowing nested calls to 'Polysemy.Conc.lock' unless called from a different+-- | Interpret 'Lock' as a reentrant lock, allowing nested calls to 'Polysemy.Conc.lock' unless called from a different -- thread (as in, @async@ was called in a higher-order action passed to 'Polysemy.Conc.lock'.) interpretLockReentrant :: Members [Resource, Race, Mask, Embed IO] r =>
lib/Polysemy/Conc/Interpreter/Mask.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Mask Interpreters, Internal+-- | Description: Mask Interpreters, Internal module Polysemy.Conc.Interpreter.Mask where import qualified Control.Exception as Base@@ -8,9 +8,9 @@ import Polysemy.Conc.Effect.Mask ( Mask,+ MaskMode (Interruptible, Uninterruptible), Restoration (Restoration), RestoreMask (Restore),- UninterruptibleMask, ) mask ::@@ -39,28 +39,42 @@ Restore ma -> withStrategicToFinal (restore <$> runS (runTSimple ma)) --- |Interpret 'Mask' by sequencing the action without masking.+maskForMode ::+ Member (Final IO) r =>+ MaskMode ->+ (Restoration -> Sem r a) ->+ Sem r a+maskForMode = \case+ Interruptible -> mask+ Uninterruptible -> uninterruptibleMask++-- | Interpret 'Mask' by sequencing the action without masking. interpretMaskPure :: InterpreterFor Mask r interpretMaskPure = interpretScopedH (const ($ ())) \ () -> \case Restore ma -> runTSimple ma --- |Interpret 'Mask' in 'IO'.+-- | Interpret 'Mask' in 'IO', dispatching on 'MaskMode' to select 'Base.mask' or 'Base.uninterruptibleMask'. interpretMaskFinal :: Member (Final IO) r => InterpreterFor Mask r interpretMaskFinal =- runScoped (const mask) \ r -> interpretRestoreMask r+ runScoped maskForMode interpretRestoreMask --- |Interpret 'UninterruptibleMask' by sequencing the action without masking.-interpretUninterruptibleMaskPure :: InterpreterFor UninterruptibleMask r+-- | Interpret 'Mask' by sequencing the action without masking.+--+-- @since 0.14.1.0+{-# deprecated interpretUninterruptibleMaskPure "Use interpretMaskPure, which now handles both variants" #-}+interpretUninterruptibleMaskPure :: InterpreterFor Mask r interpretUninterruptibleMaskPure =- interpretScopedH (const ($ ())) \ () -> \case- Restore ma -> runTSimple ma+ interpretMaskPure --- |Interpret 'UninterruptibleMask' in 'IO'.+-- | Interpret 'Mask' in 'IO'.+--+-- @since 0.14.1.0+{-# deprecated interpretUninterruptibleMaskFinal "Use interpretMaskFinal, which now handles both variants" #-} interpretUninterruptibleMaskFinal :: Member (Final IO) r =>- InterpreterFor UninterruptibleMask r+ InterpreterFor Mask r interpretUninterruptibleMaskFinal =- runScoped (const uninterruptibleMask) \ r -> interpretRestoreMask r+ interpretMaskFinal
lib/Polysemy/Conc/Interpreter/Monitor.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Monitor Interpreters, Internal+-- | Description: Monitor Interpreters, Internal module Polysemy.Conc.Interpreter.Monitor where import qualified Control.Exception as Base@@ -18,8 +18,8 @@ import qualified Polysemy.Conc.Effect.Race as Race import Polysemy.Conc.Effect.Race (Race) -newtype CancelResource =- CancelResource { signal :: MVar () }+data CancelResource =+ CancelResource { kill :: MVar () } data MonitorCancel = MonitorCancel@@ -33,15 +33,24 @@ (CancelResource -> Sem r a) -> Sem r a monitorRestart (MonitorCheck interval check) use = do- sig <- embedFinal @IO newEmptyMVar- withAsync_ (Time.loop_ @t @d interval (check sig)) (spin sig)- where- spin sig = do- let res = (CancelResource sig)- void (embedFinal @IO (tryTakeMVar sig))- either (const (spin sig)) pure =<< errorToIOFinal @MonitorCancel (fromExceptionSem @MonitorCancel (raise (use res)))+ kill <- embedFinal @IO newEmptyMVar+ start <- embedFinal @IO newEmptyMVar+ let --- |Interpret @'Polysemy.Conc.Scoped' 'Monitor'@ with the 'Polysemy.Conc.Restart' strategy.+ runCheck = do+ embedFinal (readMVar start)+ whenM check $ embedFinal @IO do+ takeMVar start+ void (tryPutMVar kill ())++ spin = do+ let res = CancelResource {..}+ embedFinal (putMVar start ())+ leftA (const spin) =<< errorToIOFinal @MonitorCancel (fromExceptionSem @MonitorCancel (raise (use res)))++ withAsync_ (Time.loop_ @t @d interval runCheck) spin++-- | Interpret @'Polysemy.Conc.Scoped' 'Monitor'@ with the 'Polysemy.Conc.Restart' strategy. -- This takes a check action that may put an 'MVar' when the scoped region should be restarted. -- The check is executed in a loop, with an interval given in 'MonitorCheck'. interpretMonitorRestart ::@@ -51,8 +60,9 @@ InterpreterFor RestartingMonitor r interpretMonitorRestart check = interpretScopedH (const (monitorRestart @t @d (hoistMonitorCheck raise check))) \ CancelResource {..} -> \case- Monitor ma ->- either (const (Base.throw MonitorCancel)) pure =<< Race.race (embedFinal @IO (readMVar signal)) (runTSimple ma)+ Monitor ma -> do+ void (embedFinal @IO (tryTakeMVar kill))+ leftA (const (Base.throw MonitorCancel)) =<< Race.race (embedFinal @IO (readMVar kill)) (runTSimple ma) interpretMonitorPure' :: () -> InterpreterFor (Monitor action) r interpretMonitorPure' _ =@@ -60,7 +70,7 @@ Monitor ma -> runTSimple ma --- |Run 'Monitor' as a no-op.+-- | Run 'Monitor' as a no-op. interpretMonitorPure :: InterpreterFor (ScopedMonitor action) r interpretMonitorPure = runScopedAs (const unit) interpretMonitorPure'
lib/Polysemy/Conc/Interpreter/Queue/Pure.hs view
@@ -1,4 +1,4 @@--- |Description: Pure Queue Interpreters+-- | Description: Pure Queue Interpreters module Polysemy.Conc.Interpreter.Queue.Pure where import Polysemy.Conc.AtomicState (interpretAtomic)@@ -7,7 +7,7 @@ import qualified Polysemy.Conc.Effect.Queue as Queue import Polysemy.Conc.Effect.Queue (Queue) --- |Reinterpret 'Queue' as 'AtomicState' with a list that cannot be written to.+-- | Reinterpret 'Queue' as 'AtomicState' with a list that cannot be written to. -- Useful for testing. interpretQueueListReadOnlyAtomicWith :: ∀ d r .@@ -48,7 +48,7 @@ h : _ -> QueueResult.Success h {-# inline interpretQueueListReadOnlyAtomicWith #-} --- |Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'AtomicState'.+-- | Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'AtomicState'. interpretQueueListReadOnlyAtomic :: ∀ d r . Member (Embed IO) r =>@@ -58,7 +58,7 @@ interpretAtomic ds (interpretQueueListReadOnlyAtomicWith (raiseUnder sem)) {-# inline interpretQueueListReadOnlyAtomic #-} --- |Reinterpret 'Queue' as 'State' with a list that cannot be written to.+-- | Reinterpret 'Queue' as 'State' with a list that cannot be written to. -- Useful for testing. interpretQueueListReadOnlyStateWith :: ∀ d r .@@ -99,7 +99,7 @@ h : _ -> QueueResult.Success h {-# inline interpretQueueListReadOnlyStateWith #-} --- |Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'State'.+-- | Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'State'. interpretQueueListReadOnlyState :: ∀ d r . [d] ->
lib/Polysemy/Conc/Interpreter/Queue/TB.hs view
@@ -1,4 +1,4 @@--- |Description: Queue Interpreters for 'TBQueue'+-- | Description: Queue Interpreters for 'TBQueue' module Polysemy.Conc.Interpreter.Queue.TB where import Control.Concurrent.STM (@@ -20,7 +20,7 @@ import Polysemy.Conc.Queue.Result (naResult) import Polysemy.Conc.Queue.Timeout (withTimeout) --- |Interpret 'Queue' with a 'TBQueue'.+-- | Interpret 'Queue' with a 'TBQueue'. -- -- This variant expects an allocated queue as an argument. interpretQueueTBWith ::@@ -53,11 +53,11 @@ unit {-# inline interpretQueueTBWith #-} --- |Interpret 'Queue' with a 'TBQueue'.+-- | Interpret 'Queue' with a 'TBQueue'. interpretQueueTB :: ∀ d r . Members [Race, Embed IO] r =>- -- |Buffer size+ -- | Buffer size Natural -> InterpreterFor (Queue d) r interpretQueueTB maxQueued sem = do
lib/Polysemy/Conc/Interpreter/Queue/TBM.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Queue Interpreters for 'TBMQueue'+-- | Description: Queue Interpreters for 'TBMQueue' module Polysemy.Conc.Interpreter.Queue.TBM where import Control.Concurrent.STM (atomically)@@ -23,7 +23,7 @@ import Polysemy.Conc.Queue.Result (closedBoolResult, closedNaResult, closedResult) import Polysemy.Conc.Queue.Timeout (withTimeout) --- |Interpret 'Queue' with a 'TBMQueue'.+-- | Interpret 'Queue' with a 'TBMQueue'. -- -- This variant expects an allocated queue as an argument. interpretQueueTBMWith ::@@ -65,11 +65,11 @@ withTBMQueue maxQueued = bracket (embed (newTBMQueueIO maxQueued)) (embed . atomically . closeTBMQueue) --- |Interpret 'Queue' with a 'TBMQueue'.+-- | Interpret 'Queue' with a 'TBMQueue'. interpretQueueTBM :: ∀ d r . Members [Resource, Race, Embed IO] r =>- -- |Buffer size+ -- | Buffer size Int -> InterpreterFor (Queue d) r interpretQueueTBM maxQueued sem = do
lib/Polysemy/Conc/Interpreter/Race.hs view
@@ -1,5 +1,5 @@ {-# options_haddock prune #-}--- |Description: Race Interpreters+-- | Description: Race Interpreters module Polysemy.Conc.Interpreter.Race where import qualified Control.Concurrent.Async as Async@@ -19,7 +19,7 @@ either (fmap Left) (fmap Right) {-# inline biseqEither #-} --- |Interpret 'Race' in terms of 'Async.race' and 'System.timeout'.+-- | 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 =>
− lib/Polysemy/Conc/Interpreter/Scoped.hs
@@ -1,640 +0,0 @@-{-# options_haddock prune #-}---- |Description: Scoped Interpreters, Internal-module Polysemy.Conc.Interpreter.Scoped where--import GHC.Err (errorWithoutStackTrace)-import Polysemy.Internal (Sem (Sem), hoistSem, liftSem, runSem)-import Polysemy.Internal.Sing (KnownList (singList))-import Polysemy.Internal.Tactics (liftT)-import Polysemy.Internal.Union (- Union (Union),- Weaving (Weaving),- decomp,- extendMembershipLeft,- hoist,- injWeaving,- injectMembership,- weave,- )-import Polysemy.Membership (ElemOf)-import Polysemy.Resume (Stop, interpretResumableH, runStop, type (!!))-import Polysemy.Resume.Effect.Resumable (Resumable (Resumable))-import Prelude hiding (Scoped, interpretScoped, interpretScopedH, interpretScopedWithH, runScoped)--import Polysemy.Conc.Effect.Scoped (Scoped (InScope, Run))--interpretWeaving ::- ∀ e r .- (∀ x . Weaving e (Sem (e : r)) x -> Sem r x) ->- InterpreterFor e r-interpretWeaving h (Sem m) =- Sem \ k -> m $ decomp >>> \case- Right wav -> runSem (h wav) k- Left g -> k (hoist (interpretWeaving h) g)--restack :: (∀ e . ElemOf e r -> ElemOf e r')- -> Sem r a- -> Sem r' a-restack n =- hoistSem $ \ (Union pr wav) -> hoist (restack n) (Union (n pr) wav)-{-# inline restack #-}--exResumable ::- Functor f =>- f () ->- (f (Either err a) -> x) ->- (f' a' -> a) ->- Sem r (Either err (f (f' a'))) ->- Sem r x-exResumable s ex ex' =- fmap $ ex . \case- Right a -> Right . ex' <$> a- Left err -> Left err <$ s-{-# inline exResumable #-}---- | Construct an interpreter for a higher-order effect wrapped in a 'Scoped',--- given a resource allocation function and a parameterized handler for the--- plain effect.------ This combinator is analogous to 'interpretH' in that it allows the handler to--- use the 'Tactical' environment and transforms the effect into other effects--- on the stack.-interpretScopedH ::- ∀ resource param effect r .- -- | A callback function that allows the user to acquire a resource for each- -- computation wrapped by 'Polysemy.Conc.scoped' using other effects, with an additional- -- argument that contains the call site parameter passed to 'Polysemy.Conc.scoped'.- (∀ x . param -> (resource -> Sem r x) -> Sem r x) ->- -- | A handler like the one expected by 'interpretH' with an additional- -- parameter that contains the @resource@ allocated by the first argument.- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r x) ->- InterpreterFor (Scoped param effect) r-interpretScopedH withResource scopedHandler =- interpretWeaving $ \(Weaving effect s wv ex _) -> case effect of- Run _ -> errorWithoutStackTrace "top level run"- InScope param main -> withResource param \ resource ->- ex- <$> interpretH (scopedHandler resource) (go $ raiseUnder $ wv (main <$ s))- where- -- TODO investigate whether loopbreaker optimization is effective here- go :: InterpreterFor (Scoped param effect) (effect : r)- go =- interpretWeaving \ (Weaving effect s wv ex ins) -> case effect of- Run act -> liftSem $ injWeaving $ Weaving act s (go . wv) ex ins- InScope param main -> raise $ withResource param \ resource ->- ex <$> interpretH (scopedHandler resource) (go $ wv (main <$ s))-{-# inline interpretScopedH #-}---- | Variant of 'interpretScopedH' that allows the resource acquisition function--- to use 'Tactical'.-interpretScopedH' ::- ∀ resource param effect r .- (∀ e r0 x . param -> (resource -> Tactical e (Sem r0) r x) ->- Tactical e (Sem r0) r x) ->- (∀ r0 x .- resource -> effect (Sem r0) x ->- Tactical (Scoped param effect) (Sem r0) r x) ->- InterpreterFor (Scoped param effect) r-interpretScopedH' withResource scopedHandler =- go (errorWithoutStackTrace "top level run")- where- go :: resource -> InterpreterFor (Scoped param effect) r- go resource =- interpretH \case- Run act ->- scopedHandler resource act- InScope param main ->- withResource param \ resource' ->- raise . go resource' =<< runT main-{-# inline interpretScopedH' #-}---- | First-order variant of 'interpretScopedH'.-interpretScoped ::- ∀ resource param effect r .- (∀ x . param -> (resource -> Sem r x) -> Sem r x) ->- (∀ m x . resource -> effect m x -> Sem r x) ->- InterpreterFor (Scoped param effect) r-interpretScoped withResource scopedHandler =- interpretScopedH withResource \ r e -> liftT (scopedHandler r e)-{-# inline interpretScoped #-}---- | Variant of 'interpretScoped' in which the resource allocator is a plain--- action.-interpretScopedAs ::- ∀ resource param effect r .- (param -> Sem r resource) ->- (∀ m x . resource -> effect m x -> Sem r x) ->- InterpreterFor (Scoped param effect) r-interpretScopedAs resource =- interpretScoped \ p use -> use =<< resource p-{-# inline interpretScopedAs #-}---- | Higher-order interpreter for 'Scoped' that allows the handler to use--- additional effects that are interpreted by the resource allocator.------ /Note/: It is necessary to specify the list of local interpreters with a type--- application; GHC won't be able to figure them out from the type of--- @withResource@.------ As an example for a higher order effect, consider a mutexed concurrent state--- effect, where an effectful function may lock write access to the state while--- making it still possible to read it:------ > data MState s :: Effect where--- > MState :: (s -> m (s, a)) -> MState s m a--- > MRead :: MState s m s--- >--- > makeSem ''MState------ We can now use an 'Polysemy.AtomicState.AtomicState' to store the current--- value and lock write access with an @MVar@. Since the state callback is--- effectful, we need a higher order interpreter:------ > withResource ::--- > Member (Embed IO) r =>--- > s ->--- > (MVar () -> Sem (AtomicState s : r) a) ->--- > Sem r a--- > withResource initial use = do--- > tv <- embed (newTVarIO initial)--- > lock <- embed (newMVar ())--- > runAtomicStateTVar tv $ use lock--- >--- > interpretMState ::--- > ∀ s r .--- > Members [Resource, Embed IO] r =>--- > InterpreterFor (Scoped s (MState s)) r--- > interpretMState =--- > interpretScopedWithH @'[AtomicState s] withResource \ lock -> \case--- > MState f ->--- > bracket_ (embed (takeMVar lock)) (embed (tryPutMVar lock ())) do--- > s0 <- atomicGet--- > res <- runTSimple (f s0)--- > Inspector ins <- getInspectorT--- > for_ (ins res) \ (s, _) -> atomicPut s--- > pure (snd <$> res)--- > MRead ->--- > liftT atomicGet-interpretScopedWithH ::- ∀ extra resource param effect r r1 .- KnownList extra =>- r1 ~ (extra ++ r) =>- (∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r1 x) ->- InterpreterFor (Scoped param effect) r-interpretScopedWithH withResource scopedHandler =- interpretWeaving \case- Weaving (InScope param main) s wv ex _ ->- ex <$> withResource param \ resource ->- interpretH (scopedHandler resource) $ inScope $- restack- (injectMembership- (singList @'[Scoped param effect])- (singList @(effect : extra))) $ wv (main <$ s)- _ ->- errorWithoutStackTrace "top level Run"- where- inScope :: InterpreterFor (Scoped param effect) (effect : r1)- inScope =- interpretWeaving \case- Weaving (InScope param main) s wv ex _ ->- restack- (extendMembershipLeft (singList @(effect : extra)))- (ex <$> withResource param \resource ->- interpretH (scopedHandler resource) $ inScope $ wv (main <$ s))- Weaving (Run act) s wv ex ins ->- liftSem $ injWeaving $ Weaving act s (inScope . wv) ex ins-{-# inline interpretScopedWithH #-}---- | First-order variant of 'interpretScopedWithH'.------ /Note/: It is necessary to specify the list of local interpreters with a type--- application; GHC won't be able to figure them out from the type of--- @withResource@:------ > data SomeAction :: Effect where--- > SomeAction :: SomeAction m ()--- >--- > foo :: InterpreterFor (Scoped () SomeAction) r--- > foo =--- > interpretScopedWith @[Reader Int, State Bool] localEffects \ () -> \case--- > SomeAction -> put . (> 0) =<< ask @Int--- > where--- > localEffects () use = evalState False (runReader 5 (use ()))-interpretScopedWith ::- ∀ extra param resource effect r r1 .- r1 ~ (extra ++ r) =>- KnownList extra =>- (∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->- (∀ m x . resource -> effect m x -> Sem r1 x) ->- InterpreterFor (Scoped param effect) r-interpretScopedWith withResource scopedHandler =- interpretScopedWithH @extra withResource \ r e -> liftT (scopedHandler r e)-{-# inline interpretScopedWith #-}---- | Variant of 'interpretScopedWith' in which no resource is used and the--- resource allocator is a plain interpreter.--- This is useful for scopes that only need local effects, but no resources in--- the handler.------ See the /Note/ on 'interpretScopedWithH'.-interpretScopedWith_ ::- ∀ extra param effect r r1 .- r1 ~ (extra ++ r) =>- KnownList extra =>- (∀ x . param -> Sem r1 x -> Sem r x) ->- (∀ m x . effect m x -> Sem r1 x) ->- InterpreterFor (Scoped param effect) r-interpretScopedWith_ withResource scopedHandler =- interpretScopedWithH @extra (\ p f -> withResource p (f ())) \ () e -> liftT (scopedHandler e)-{-# inline interpretScopedWith_ #-}---- | Variant of 'interpretScoped' that uses another interpreter instead of a--- handler.------ This is mostly useful if you want to reuse an interpreter that you cannot--- easily rewrite (like from another library). If you have full control over the--- implementation, 'interpretScoped' should be preferred.------ /Note/: In previous versions of Polysemy, the wrapped interpreter was--- executed fully, including the initializing code surrounding its handler,--- for each action in the program. However, new and continuing discoveries--- regarding 'Scoped' has allowed the improvement of having the interpreter be--- used only once per use of 'scoped', and have it cover the same scope of--- actions that @withResource@ does.------ This renders @withResource@ practically redundant; for the moment, the API--- surrounding 'Scoped' remains the same, but work is in progress to revamp the--- entire API of 'Scoped'.-runScoped ::- ∀ resource param effect r .- (∀ x . param -> (resource -> Sem r x) -> Sem r x) ->- (resource -> InterpreterFor effect r) ->- InterpreterFor (Scoped param effect) r-runScoped withResource scopedInterpreter =- interpretWeaving \(Weaving effect s wv ex _) -> case effect of- Run _ -> errorWithoutStackTrace "top level run"- InScope param main -> withResource param \ resource ->- ex <$> scopedInterpreter resource (go (raiseUnder $ wv (main <$ s)))- where- go :: InterpreterFor (Scoped param effect) (effect : r)- go =- interpretWeaving \ (Weaving effect s wv ex ins) -> case effect of- Run act -> liftSem $ injWeaving $ Weaving act s (go . wv) ex ins- InScope param main ->- raise $ withResource param \ resource ->- ex <$> scopedInterpreter resource (go (wv (main <$ s)))-{-# inline runScoped #-}---- | Variant of 'runScoped' in which the resource allocator returns the resource--- rather tnen calling a continuation.-runScopedAs ::- ∀ resource param effect r .- (param -> Sem r resource) ->- (resource -> InterpreterFor effect r) ->- InterpreterFor (Scoped param effect) r-runScopedAs resource = runScoped \ p use -> use =<< resource p-{-# inline runScopedAs #-}---- |Combined higher-order interpreter for 'Scoped' and 'Resumable'.--- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the--- entire scope.-interpretScopedResumableH ::- ∀ param resource effect err r .- (∀ x . param -> (resource -> Sem (Stop err : r) x) -> Sem (Stop err : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) (Stop err : r) x) ->- InterpreterFor (Scoped param effect !! err) r-interpretScopedResumableH withResource scopedHandler =- interpretWeaving \case- Weaving (Resumable (Weaving (InScope param main) s' wv' ex' _)) s wv ex _ -> do- exResumable s ex ex' $ runStop $ withResource param \ resource ->- interpretH (scopedHandler resource) (raiseUnder (go $ raiseUnder $ wv ((wv' (main <$ s')) <$ s)))- _ ->- errorWithoutStackTrace "top level run"- where- go :: InterpreterFor (Scoped param effect !! err) (effect : r)- go =- interpretWeaving \ (Weaving (Resumable (Weaving effect s' wv' ex' ins')) s wv ex ins) -> case effect of- Run act ->- ex . fmap Right <$> liftSem (weave s (go . wv) ins (injWeaving (Weaving act s' wv' ex' ins')))- InScope param main -> do- exResumable s ex ex' $ raise $ runStop $ withResource param \ resource ->- interpretH (scopedHandler resource) (raiseUnder (go $ wv (wv' (main <$ s') <$ s)))---- |Combined interpreter for 'Scoped' and 'Resumable'.--- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the--- entire scope.-interpretScopedResumable ::- ∀ param resource effect err r .- (∀ x . param -> (resource -> Sem (Stop err : r) x) -> Sem (Stop err : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->- InterpreterFor (Scoped param effect !! err) r-interpretScopedResumable withResource scopedHandler =- interpretScopedResumableH withResource \ r e -> liftT (scopedHandler r e)---- |Combined interpreter for 'Scoped' and 'Resumable'.--- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the--- entire scope.--- In this variant, the resource allocator is a plain action.-interpretScopedResumable_ ::- ∀ param resource effect err r .- (param -> Sem (Stop err : r) resource) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->- InterpreterFor (Scoped param effect !! err) r-interpretScopedResumable_ resource =- interpretScopedResumable \ p use -> use =<< resource p---- |Combined higher-order interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects--- that are interpreted by the resource allocator.--- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the--- entire scope.-interpretScopedResumableWithH ::- ∀ extra param resource effect err r r1 extraerr .- extraerr ~ (extra ++ '[Stop err]) =>- r1 ~ (extraerr ++ r) =>- KnownList (extra ++ '[Stop err]) =>- (∀ x . param -> (resource -> Sem r1 x) -> Sem (Stop err : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r1 x) ->- InterpreterFor (Scoped param effect !! err) r-interpretScopedResumableWithH withResource scopedHandler =- interpretWeaving \case- Weaving (Resumable (Weaving (InScope param main) s' wv' ex' _)) s wv ex _ -> do- exResumable s ex ex' $ runStop $ withResource param \ resource ->- interpretH (scopedHandler resource) $ inScope $- restack- (injectMembership- (singList @'[Scoped param effect !! err])- (singList @(effect : extraerr))) $ wv (wv' (main <$ s') <$ s)- _ ->- errorWithoutStackTrace "top level Run"- where- inScope :: InterpreterFor (Scoped param effect !! err) (effect : r1)- inScope =- interpretWeaving \ (Weaving (Resumable (Weaving effect s' wv' ex' ins')) s wv ex ins) -> case effect of- InScope param main ->- restack- (extendMembershipLeft (singList @(effect : extraerr))) $- exResumable s ex ex' $ runStop $ withResource param \ resource ->- interpretH (scopedHandler resource) (inScope $ wv (wv' (main <$ s') <$ s))- Run act ->- ex . fmap Right <$> liftSem (weave s (inScope . wv) ins (injWeaving (Weaving act s' wv' ex' ins')))---- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are--- interpreted by the resource allocator.--- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the--- entire scope.-interpretScopedResumableWith ::- ∀ extra param resource effect err r r1 .- r1 ~ ((extra ++ '[Stop err]) ++ r) =>- KnownList (extra ++ '[Stop err]) =>- (∀ x . param -> (resource -> Sem r1 x) -> Sem (Stop err : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem r1 x) ->- InterpreterFor (Scoped param effect !! err) r-interpretScopedResumableWith withResource scopedHandler =- interpretScopedResumableWithH @extra withResource \ r e -> liftT (scopedHandler r e)---- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are--- interpreted by the resource allocator.--- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the--- entire scope.--- In this variant, no resource is used and the allocator is a plain interpreter.-interpretScopedResumableWith_ ::- ∀ extra param effect err r r1 .- r1 ~ ((extra ++ '[Stop err]) ++ r) =>- KnownList (extra ++ '[Stop err]) =>- (∀ x . param -> Sem r1 x -> Sem (Stop err : r) x) ->- (∀ r0 x . effect (Sem r0) x -> Sem r1 x) ->- InterpreterFor (Scoped param effect !! err) r-interpretScopedResumableWith_ extra scopedHandler =- interpretScopedResumableWith @extra (\ p f -> extra p (f ())) (const scopedHandler)---- |Combined higher-order interpreter for 'Resumable' and 'Scoped'.--- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the--- scope.-interpretResumableScopedH ::- ∀ param resource effect err r .- (∀ x . param -> (resource -> Sem r x) -> Sem r x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical (effect !! err) (Sem r0) (Stop err : r) x) ->- InterpreterFor (Scoped param (effect !! err)) r-interpretResumableScopedH withResource scopedHandler =- interpretWeaving $ \(Weaving effect s wv ex _) -> case effect of- Run _ -> errorWithoutStackTrace "top level run"- InScope param main -> withResource param \ resource ->- ex <$> interpretResumableH (scopedHandler resource) (inScope $ raiseUnder $ wv (main <$ s))- where- inScope :: InterpreterFor (Scoped param (effect !! err)) (effect !! err : r)- inScope =- interpretWeaving \ (Weaving effect s wv ex ins) -> case effect of- Run act -> liftSem $ injWeaving $ Weaving act s (inScope . wv) ex ins- InScope param main -> raise $ withResource param \ resource ->- ex <$> interpretResumableH (scopedHandler resource) (inScope $ wv (main <$ s))---- |Combined interpreter for 'Resumable' and 'Scoped'.--- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the--- scope.-interpretResumableScoped ::- ∀ param resource effect err r .- (∀ x . param -> (resource -> Sem r x) -> Sem r x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->- InterpreterFor (Scoped param (effect !! err)) r-interpretResumableScoped withResource scopedHandler =- interpretResumableScopedH withResource \ r e -> liftT (scopedHandler r e)---- |Combined interpreter for 'Resumable' and 'Scoped'.--- In this variant:--- - Only the handler may send 'Stop', but this allows resumption to happen on each action inside of the scope.--- - The resource allocator is a plain action.-interpretResumableScoped_ ::- ∀ param resource effect err r .- (param -> Sem r resource) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->- InterpreterFor (Scoped param (effect !! err)) r-interpretResumableScoped_ resource =- interpretResumableScoped \ p use -> use =<< resource p---- |Combined higher-order interpreter for 'Resumable' and 'Scoped' that allows the handler to use additional effects--- that are interpreted by the resource allocator.--- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the--- scope.-interpretResumableScopedWithH ::- ∀ extra param resource effect err r r1 .- r1 ~ (extra ++ r) =>- KnownList extra =>- (∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical (effect !! err) (Sem r0) (Stop err : r1) x) ->- InterpreterFor (Scoped param (effect !! err)) r-interpretResumableScopedWithH withResource scopedHandler =- interpretWeaving \case- Weaving (InScope param main) s wv ex _ ->- ex <$> withResource param \ resource ->- interpretResumableH (scopedHandler resource) $ inScope $- restack- (injectMembership- (singList @'[Scoped param (effect !! err)])- (singList @(effect !! err : extra))) $ wv (main <$ s)- _ ->- errorWithoutStackTrace "top level Run"- where- inScope :: InterpreterFor (Scoped param (effect !! err)) (effect !! err : r1)- inScope =- interpretWeaving \case- Weaving (InScope param main) s wv ex _ ->- restack- (extendMembershipLeft (singList @(effect !! err : extra)))- (ex <$> withResource param \resource ->- interpretResumableH (scopedHandler resource) $ inScope $ wv (main <$ s))- Weaving (Run act) s wv ex ins ->- liftSem $ injWeaving $ Weaving act s (inScope . wv) ex ins---- |Combined interpreter for 'Resumable' and 'Scoped' that allows the handler to use additional effects that are--- interpreted by the resource allocator.--- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the--- scope.-interpretResumableScopedWith ::- ∀ extra param resource effect err r r1 .- r1 ~ (extra ++ r) =>- KnownList extra =>- (∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r1) x) ->- InterpreterFor (Scoped param (effect !! err)) r-interpretResumableScopedWith withResource scopedHandler =- interpretResumableScopedWithH @extra withResource \ r e -> liftT (scopedHandler r e)---- |Combined interpreter for 'Resumable' and 'Scoped' that allows the handler to use additional effects that are--- interpreted by the resource allocator.--- In this variant:--- - Only the handler may send 'Stop', but this allows resumption to happen on each action inside of the scope.--- - No resource is used and the allocator is a plain interpreter.-interpretResumableScopedWith_ ::- ∀ extra param effect err r r1 .- r1 ~ (extra ++ r) =>- KnownList extra =>- (∀ x . param -> Sem r1 x -> Sem r x) ->- (∀ r0 x . effect (Sem r0) x -> Sem (Stop err : r1) x) ->- InterpreterFor (Scoped param (effect !! err)) r-interpretResumableScopedWith_ extra scopedHandler =- interpretResumableScopedWith @extra @param @() @effect @err @r @r1 (\ p f -> extra p (f ())) (const scopedHandler)---- |Combined higher-order interpreter for 'Resumable' and 'Scoped'.--- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the--- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,--- continuing the scope execution on resumption.-interpretScopedRH ::- ∀ param resource effect eo ei r .- (∀ x . param -> (resource -> Sem (Stop eo : r) x) -> Sem (Stop eo : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical (effect !! ei) (Sem r0) (Stop ei : Stop eo : r) x) ->- InterpreterFor (Scoped param (effect !! ei) !! eo) r-interpretScopedRH withResource scopedHandler =- interpretWeaving \case- Weaving (Resumable (Weaving (InScope param main) s' wv' ex' _)) s wv ex _ -> do- exResumable s ex ex' $ runStop $ withResource param \ resource ->- interpretResumableH (scopedHandler resource) (raiseUnder (inScope $ raiseUnder $ wv (wv' (main <$ s') <$ s)))- _ ->- errorWithoutStackTrace "top level run"- where- inScope :: InterpreterFor (Scoped param (effect !! ei) !! eo) (effect !! ei : r)- inScope =- interpretWeaving \ (Weaving (Resumable (Weaving effect s' wv' ex' ins')) s wv ex ins) -> case effect of- Run act ->- ex . fmap Right <$> liftSem (weave s (inScope . wv) ins (injWeaving (Weaving act s' wv' ex' ins')))- InScope param main -> do- exResumable s ex ex' $ raise $ runStop $ withResource param \ resource ->- interpretResumableH (scopedHandler resource) (raiseUnder (inScope $ wv (wv' (main <$ s') <$ s)))---- |Combined interpreter for 'Scoped' and 'Resumable'.--- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the--- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,--- continuing the scope execution on resumption.-interpretScopedR ::- ∀ param resource effect eo ei r .- (∀ x . param -> (resource -> Sem (Stop eo : r) x) -> Sem (Stop eo : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop ei : Stop eo : r) x) ->- InterpreterFor (Scoped param (effect !! ei) !! eo) r-interpretScopedR withResource scopedHandler =- interpretScopedRH withResource \ r e -> liftT (scopedHandler r e)---- |Combined interpreter for 'Scoped' and 'Resumable'.--- In this variant:--- - Both the handler and the scope may send different errors via 'Stop', encoding the concept that the--- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,--- continuing the scope execution on resumption.--- - The resource allocator is a plain action.-interpretScopedR_ ::- ∀ param resource effect eo ei r .- (param -> Sem (Stop eo : r) resource) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop ei : Stop eo : r) x) ->- InterpreterFor (Scoped param (effect !! ei) !! eo) r-interpretScopedR_ resource =- interpretScopedR \ p use -> use =<< resource p---- |Combined higher-order interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects--- that are interpreted by the resource allocator.--- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the--- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,--- continuing the scope execution on resumption.-interpretScopedRWithH ::- ∀ extra param resource effect eo ei r r1 extraerr .- extraerr ~ (extra ++ '[Stop eo]) =>- r1 ~ (extra ++ Stop eo : r) =>- r1 ~ ((extra ++ '[Stop eo]) ++ r) =>- KnownList (extra ++ '[Stop eo]) =>- (∀ x . param -> (resource -> Sem (extra ++ Stop eo : r) x) -> Sem (Stop eo : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Tactical (effect !! ei) (Sem r0) (Stop ei : r1) x) ->- InterpreterFor (Scoped param (effect !! ei) !! eo) r-interpretScopedRWithH withResource scopedHandler =- interpretWeaving \case- Weaving (Resumable (Weaving (InScope param main) s' wv' ex' _)) s wv ex _ -> do- exResumable s ex ex' $ runStop $ withResource param \ resource ->- interpretResumableH (scopedHandler resource) $ inScope $- restack- (injectMembership- (singList @'[Scoped param (effect !! ei) !! eo])- (singList @(effect !! ei : extraerr))) $ wv (wv' (main <$ s') <$ s)- _ ->- errorWithoutStackTrace "top level Run"- where- inScope :: InterpreterFor (Scoped param (effect !! ei) !! eo) (effect !! ei : r1)- inScope =- interpretWeaving \ (Weaving (Resumable (Weaving effect s' wv' ex' ins')) s wv ex ins) -> case effect of- InScope param main ->- exResumable s ex ex' $- restack (extendMembershipLeft (singList @(effect !! ei : extraerr))) $- runStop $- withResource param \ resource ->- interpretResumableH (scopedHandler resource) (inScope $ wv (wv' (main <$ s') <$ s))- Run act ->- ex . fmap Right <$> liftSem (weave s (inScope . wv) ins (injWeaving (Weaving act s' wv' ex' ins')))---- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are--- interpreted by the resource allocator.--- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the--- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,--- continuing the scope execution on resumption.-interpretScopedRWith ::- ∀ extra param resource effect eo ei r r1 .- r1 ~ (extra ++ Stop eo : r) =>- r1 ~ ((extra ++ '[Stop eo]) ++ r) =>- KnownList (extra ++ '[Stop eo]) =>- (∀ x . param -> (resource -> Sem r1 x) -> Sem (Stop eo : r) x) ->- (∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop ei : r1) x) ->- InterpreterFor (Scoped param (effect !! ei) !! eo) r-interpretScopedRWith withResource scopedHandler =- interpretScopedRWithH @extra withResource \ r e -> liftT (scopedHandler r e)---- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are--- interpreted by the resource allocator.--- - Both the handler and the scope may send different errors via 'Stop', encoding the concept that the--- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,--- continuing the scope execution on resumption.--- - The resource allocator is a plain action.-interpretScopedRWith_ ::- ∀ extra param effect eo ei r r1 .- r1 ~ (extra ++ Stop eo : r) =>- r1 ~ ((extra ++ '[Stop eo]) ++ r) =>- KnownList (extra ++ '[Stop eo]) =>- (∀ x . param -> Sem r1 x -> Sem (Stop eo : r) x) ->- (∀ r0 x . effect (Sem r0) x -> Sem (Stop ei : r1) x) ->- InterpreterFor (Scoped param (effect !! ei) !! eo) r-interpretScopedRWith_ extra scopedHandler =- interpretScopedRWith @extra (\ p f -> extra p (f ())) (const scopedHandler)
lib/Polysemy/Conc/Interpreter/Semaphore.hs view
@@ -1,4 +1,4 @@--- |Semaphore interpreters, Internal.+-- | Semaphore interpreters, Internal. module Polysemy.Conc.Interpreter.Semaphore where import Control.Concurrent (QSem, newQSem, signalQSem, waitQSem)@@ -8,7 +8,7 @@ import qualified Polysemy.Conc.Effect.Semaphore as Semaphore import Polysemy.Conc.Effect.Semaphore (Semaphore) --- |Interpret 'Semaphore' using the supplied 'QSem'.+-- | Interpret 'Semaphore' using the supplied 'QSem'. interpretSemaphoreQWith :: Member (Embed IO) r => QSem ->@@ -21,7 +21,7 @@ embed (signalQSem qsem) {-# inline interpretSemaphoreQWith #-} --- |Interpret 'Semaphore' as a 'QSem'.+-- | Interpret 'Semaphore' as a 'QSem'. interpretSemaphoreQ :: Member (Embed IO) r => Int ->@@ -31,7 +31,7 @@ interpretSemaphoreQWith qsem sem {-# inline interpretSemaphoreQ #-} --- |Interpret 'Semaphore' using the supplied 'TSem'.+-- | Interpret 'Semaphore' using the supplied 'TSem'. interpretSemaphoreTWith :: Member (Embed IO) r => TSem ->@@ -44,7 +44,7 @@ embed (atomically (signalTSem qsem)) {-# inline interpretSemaphoreTWith #-} --- |Interpret 'Semaphore' as a 'TSem'.+-- | Interpret 'Semaphore' as a 'TSem'. interpretSemaphoreT :: Member (Embed IO) r => Integer ->
lib/Polysemy/Conc/Interpreter/Stack.hs view
@@ -1,19 +1,18 @@ {-# options_haddock prune #-} --- |Description: Convenience Interpreters for all Conc Effects, Internal+-- | Description: Convenience Interpreters for all Conc Effects, Internal module Polysemy.Conc.Interpreter.Stack where import Polysemy.Conc.Effect.Gate (Gates)-import Polysemy.Conc.Effect.Mask (Mask, UninterruptibleMask)+import Polysemy.Conc.Effect.Mask (Mask) import Polysemy.Conc.Effect.Race (Race) import Polysemy.Conc.Interpreter.Gate (interpretGates)-import Polysemy.Conc.Interpreter.Mask (interpretMaskFinal, interpretUninterruptibleMaskFinal)+import Polysemy.Conc.Interpreter.Mask (interpretMaskFinal) import Polysemy.Conc.Interpreter.Race (interpretRace) --- |A default basic stack with 'Final' for _polysemy-conc_.+-- | A default basic stack with 'Final' for _polysemy-conc_. type ConcStack = [- UninterruptibleMask, Mask, Gates, Race,@@ -23,7 +22,7 @@ Final IO ] --- |Interprets 'UninterruptibleMask', 'Mask' and 'Race' in terms of @'Final' 'IO'@ and runs the entire rest of the+-- | Interprets 'Mask' and 'Race' in terms of @'Final' 'IO'@ and runs the entire rest of the -- stack. runConc :: Sem ConcStack a ->@@ -35,5 +34,4 @@ asyncToIOFinal . interpretRace . interpretGates .- interpretMaskFinal .- interpretUninterruptibleMaskFinal+ interpretMaskFinal
lib/Polysemy/Conc/Interpreter/Sync.hs view
@@ -1,4 +1,4 @@--- |Description: Sync Interpreters+-- | Description: Sync Interpreters module Polysemy.Conc.Interpreter.Sync where import Polysemy.Conc.Effect.Race (Race)@@ -6,7 +6,7 @@ import Polysemy.Conc.Effect.Sync (Sync) import qualified Polysemy.Conc.Race as Race --- |Interpret 'Sync' with the provided 'MVar'.+-- | Interpret 'Sync' with the provided 'MVar'. interpretSyncWith :: ∀ d r . Members [Race, Embed IO] r =>@@ -35,7 +35,7 @@ Sync.Empty -> embed (isEmptyMVar var) --- |Interpret 'Sync' with an empty 'MVar'.+-- | Interpret 'Sync' with an empty 'MVar'. interpretSync :: ∀ d r . Members [Race, Embed IO] r =>@@ -44,7 +44,7 @@ var <- embed newEmptyMVar interpretSyncWith var sem --- |Interpret 'Sync' with an 'MVar' containing the specified value.+-- | Interpret 'Sync' with an 'MVar' containing the specified value. interpretSyncAs :: ∀ d r . Members [Race, Embed IO] r =>@@ -54,7 +54,7 @@ var <- embed (newMVar d) interpretSyncWith var sem --- |Interpret 'Sync' for locally scoped use with an empty 'MVar'.+-- | Interpret 'Sync' for locally scoped use with an empty 'MVar'. interpretScopedSync :: ∀ d r . Members [Resource, Race, Embed IO] r =>@@ -62,7 +62,7 @@ interpretScopedSync = runScopedAs (const (embed newEmptyMVar)) \ r -> interpretSyncWith r --- |Interpret 'Sync' for locally scoped use with an 'MVar' containing the specified value.+-- | Interpret 'Sync' for locally scoped use with an 'MVar' containing the specified value. interpretScopedSyncAs :: ∀ d r . Members [Resource, Race, Embed IO] r =>
lib/Polysemy/Conc/Interpreter/SyncRead.hs view
@@ -1,4 +1,4 @@--- |Description: Interpreter for 'SyncRead' that reinterprets to 'Sync'.+-- | Description: Interpreter for 'SyncRead' that reinterprets to 'Sync'. module Polysemy.Conc.Interpreter.SyncRead where import qualified Polysemy.Conc.Effect.Sync as Sync@@ -6,7 +6,7 @@ import qualified Polysemy.Conc.Effect.SyncRead as SyncRead import Polysemy.Conc.Effect.SyncRead (SyncRead) --- |Run 'SyncRead' in terms of 'Sync'.+-- | Run 'SyncRead' in terms of 'Sync'. syncRead :: ∀ d r . Member (Sync d) r =>
lib/Polysemy/Conc/Monitor.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Monitor Implementations, Internal+-- | Description: Monitor Implementations, Internal module Polysemy.Conc.Monitor where import qualified Polysemy.Time as Time@@ -9,7 +9,7 @@ import Polysemy.Conc.Effect.Monitor (MonitorCheck (MonitorCheck)) --- |Config for 'monitorClockSkew'.+-- | Config for 'monitorClockSkew'. data ClockSkewConfig = ClockSkewConfig { interval :: NanoSeconds,@@ -17,7 +17,7 @@ } deriving stock (Eq, Show) --- |Smart constructor for 'ClockSkewConfig' that takes arbitrary 'TimeUnit's.+-- | Smart constructor for 'ClockSkewConfig' that takes arbitrary 'TimeUnit's. clockSkewConfig :: TimeUnit u1 => TimeUnit u2 =>@@ -31,7 +31,7 @@ def = clockSkewConfig (Minutes 1) (Seconds 5) --- |Check for 'Polysemy.Conc.Effect.Monitor' that checks every @interval@ whether the difference between the current+-- | Check for 'Polysemy.Conc.Effect.Monitor' that checks every @interval@ whether the difference between the current -- time and the time at the last check is larger than @interval@ + @tolerance@. -- Can be used to detect that the operating system suspended and resumed. monitorClockSkew ::@@ -42,12 +42,8 @@ ClockSkewConfig -> MonitorCheck r monitorClockSkew (ClockSkewConfig interval tolerance) =- MonitorCheck interval \ signal -> do- atomicGet >>= \case- Just prev -> do- now <- Time.now @t @d- when (minus (convert (difference now prev)) tolerance > interval) (void (embed @IO (tryPutMVar signal ())))- atomicPut (Just now)- Nothing -> do- now <- Time.now @t @d- atomicPut (Just now)+ MonitorCheck interval do+ now <- Time.now @t @d+ atomicState \ s -> (Just now, maybe False (skewed now) s)+ where+ skewed now prev = minus (convert (difference now prev)) tolerance > interval
lib/Polysemy/Conc/Queue.hs view
@@ -1,5 +1,5 @@ {-# options_haddock prune #-}--- |Description: Queue Combinators+-- | Description: Queue Combinators module Polysemy.Conc.Queue ( module Polysemy.Conc.Queue, module Polysemy.Conc.Effect.Queue,@@ -24,7 +24,7 @@ ) import Polysemy.Conc.Queue.Result (resultToMaybe) --- |Read from a 'Queue' repeatedly until it is closed.+-- | Read from a 'Queue' repeatedly until it is closed. -- -- When an element is received, call @action@ and recurse if it returns 'True'. -- When no element is available, evaluate @na@ and recurse if it returns 'True'.@@ -42,7 +42,7 @@ QueueResult.NotAvailable -> whenM na spin QueueResult.Closed -> unit --- |Read from a 'Queue' repeatedly until it is closed.+-- | Read from a 'Queue' repeatedly until it is closed. -- -- When an element is received, call @action@ and recurse. loop ::@@ -52,7 +52,7 @@ loop action = loopOr (pure True) \ d -> True <$ action d --- |Read from a 'Queue' and convert the result to 'Maybe', returning 'Nothing' if the queue has been closed, and+-- | Read from a 'Queue' and convert the result to 'Maybe', returning 'Nothing' if the queue has been closed, and -- blocking until an element is available. readMaybe :: Member (Queue d) r =>@@ -60,7 +60,7 @@ readMaybe = resultToMaybe <$> read --- |Read from a 'Queue' and convert the result to 'Maybe', returning 'Nothing' if there is no element available or the+-- | Read from a 'Queue' and convert the result to 'Maybe', returning 'Nothing' if there is no element available or the -- queue has been closed. tryReadMaybe :: Member (Queue d) r =>
lib/Polysemy/Conc/Queue/Result.hs view
@@ -38,7 +38,7 @@ Just True -> QueueResult.Success () {-# inline closedBoolResult #-} --- |Turn a 'QueueResult.Success' into 'Just'.+-- | Turn a 'QueueResult.Success' into 'Just'. resultToMaybe :: QueueResult d -> Maybe d resultToMaybe = \case QueueResult.Success d -> Just d
lib/Polysemy/Conc/Queue/Timeout.hs view
@@ -1,4 +1,4 @@--- |Description: Timeout Helper+-- | Description: Timeout Helper module Polysemy.Conc.Queue.Timeout where import Control.Concurrent.STM (STM, atomically)@@ -9,7 +9,7 @@ import Polysemy.Conc.Effect.Race (Race) import qualified Polysemy.Conc.Race as Race --- |Run an 'STM' action atomically with a time limit+-- | Run an 'STM' action atomically with a time limit withTimeout :: TimeUnit t => Members [Race, Embed IO] r =>
lib/Polysemy/Conc/Race.hs view
@@ -1,4 +1,4 @@--- |Description: Race Combinators+-- | Description: Race Combinators module Polysemy.Conc.Race where import Polysemy.Time (TimeUnit)@@ -7,7 +7,7 @@ import Polysemy.Conc.Effect.Race (Race) import Polysemy.Resume (Stop, stop) --- |Specialization of 'Race.race' for the case where both actions return the same type, obviating the need for 'Either'.+-- | Specialization of 'Race.race' for the case where both actions return the same type, obviating the need for 'Either'. race_ :: Member Race r => Sem r a ->@@ -17,7 +17,7 @@ unify <$> Race.race ml mr {-# inline race_ #-} --- |Specialization of 'Race.timeout' for the case where the main action returns the same type as the fallback, obviating+-- | Specialization of 'Race.timeout' for the case where the main action returns the same type as the fallback, obviating -- the need for 'Either'. timeout_ :: TimeUnit u =>@@ -30,7 +30,7 @@ unify <$> Race.timeout err interval ma {-# inline timeout_ #-} --- |Version of `Race.timeout` that takes a pure fallback value.+-- | Version of `Race.timeout` that takes a pure fallback value. timeoutAs :: TimeUnit u => Member Race r =>@@ -42,7 +42,7 @@ Race.timeout (pure err) {-# inline timeoutAs #-} --- |Specialization of 'timeoutAs' for the case where the main action return the same type as the fallback, obviating the+-- | Specialization of 'timeoutAs' for the case where the main action return the same type as the fallback, obviating the -- need for 'Either'. timeoutAs_ :: TimeUnit u =>@@ -55,7 +55,7 @@ timeout_ (pure err) {-# inline timeoutAs_ #-} --- |Specialization of 'Race.timeout' for unit actions.+-- | Specialization of 'Race.timeout' for unit actions. timeoutU :: TimeUnit u => Member Race r =>@@ -66,7 +66,7 @@ timeout_ unit {-# inline timeoutU #-} --- |Variant of 'Race.timeout' that returns 'Maybe'.+-- | Variant of 'Race.timeout' that returns 'Maybe'. timeoutMaybe :: TimeUnit u => Member Race r =>@@ -77,7 +77,7 @@ timeoutAs_ Nothing u (Just <$> ma) {-# inline timeoutMaybe #-} --- |Variant of 'Race.timeout' that calls 'Stop' with the supplied error when the action times out.+-- | Variant of 'Race.timeout' that calls 'Stop' with the supplied error when the action times out. timeoutStop :: TimeUnit u => Members [Race, Stop err] r =>
lib/Polysemy/Conc/Retry.hs view
@@ -1,4 +1,4 @@--- |Description: Action Retrying+-- | Description: Action Retrying module Polysemy.Conc.Retry where import qualified Polysemy.Time as Time@@ -9,15 +9,15 @@ import Polysemy.Conc.Interpreter.Sync (interpretSync) import qualified Polysemy.Conc.Race as Race --- |Run an action repeatedly until it returns 'Right' or the timout has been exceeded.+-- | Run an action repeatedly until it returns 'Right' or the timout has been exceeded. retrying :: ∀ e w u t d r a . TimeUnit w => TimeUnit u => Members [Race, Time t d] r =>- -- |The timeout after which the attempt is abandoned.+ -- | The timeout after which the attempt is abandoned. w ->- -- |The waiting interval between two tries.+ -- | The waiting interval between two tries. u -> Sem r (Either e a) -> Sem r (Maybe a)@@ -32,7 +32,7 @@ Time.sleep @t @d interval spin --- |Run an action repeatedly until it returns 'Right' or the timout has been exceeded.+-- | Run an action repeatedly until it returns 'Right' or the timout has been exceeded. -- -- If the action failed at least once, the last error will be returned in case of timeout. retryingWithError ::@@ -40,9 +40,9 @@ TimeUnit w => TimeUnit u => Members [Race, Time t d, Embed IO] r =>- -- |The timeout after which the attempt is abandoned.+ -- | The timeout after which the attempt is abandoned. w ->- -- |The waiting interval between two tries.+ -- | The waiting interval between two tries. u -> Sem r (Either e a) -> Sem r (Maybe (Either e a))
lib/Polysemy/Conc/Semaphore.hs view
@@ -1,4 +1,4 @@--- |A quantity semaphore effect.+-- | A quantity semaphore effect. module Polysemy.Conc.Semaphore ( Semaphore, interpretSemaphoreQ,
lib/Polysemy/Conc/Sync.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Sync Combinators+-- | Description: Sync Combinators module Polysemy.Conc.Sync ( module Polysemy.Conc.Sync, module Polysemy.Conc.Effect.Sync,@@ -13,7 +13,7 @@ import qualified Polysemy.Conc.Effect.Sync as Sync import Polysemy.Conc.Effect.Sync --- |Run an action repeatedly until the 'Sync' variable is available.+-- | Run an action repeatedly until the 'Sync' variable is available. whileEmpty :: ∀ a r . Member (Sync a) r =>@@ -26,7 +26,7 @@ action whenM (not <$> Sync.empty @a) spin --- |Run an action repeatedly until the 'Sync' variable is available, waiting for the specified time between executions.+-- | Run an action repeatedly until the 'Sync' variable is available, waiting for the specified time between executions. whileEmptyInterval :: ∀ a u t d r . TimeUnit u =>@@ -41,7 +41,7 @@ action whenM (not <$> Sync.empty @a) (Time.sleep @t @d interval *> spin) --- |Run an action with a locally scoped 'Sync' variable.+-- | Run an action with a locally scoped 'Sync' variable. -- -- This avoids a dependency on @'Embed' 'IO'@ in application logic while still allowing the variable to be scoped. withSync ::@@ -51,7 +51,7 @@ withSync = scoped_ --- |Run the action @ma@ with an exclusive lock (mutex).+-- | Run the action @ma@ with an exclusive lock (mutex). -- When multiple threads call the action concurrently, only one is allowed to execute it at a time. -- The value @l@ is used to disambiguate the 'Sync' from other uses of the combinator. -- You can pass in something like @Proxy @"db-write"@.@@ -67,7 +67,7 @@ finally (takeBlock @l *> ma) (putTry l) {-# inline lock #-} --- |Remove the content of the 'Sync' variable if it is present.+-- | Remove the content of the 'Sync' variable if it is present. clear :: ∀ a r . Member (Sync a) r =>@@ -76,7 +76,7 @@ void (takeTry @a) {-# inline clear #-} --- |Modify a 'Sync' variable with async exceptions masked for the 'Sync' operations, but not the action.+-- | Modify a 'Sync' variable with async exceptions masked for the 'Sync' operations, but not the action. -- Allows a value to be returned. -- Equivalent to 'Control.Concurrent.MVar.modifyMVar'. modify ::@@ -91,7 +91,7 @@ b <$ putBlock a' {-# inline modify #-} --- |Modify a 'Sync' variable with async exceptions masked for the 'Sync' operations, but not the action.+-- | Modify a 'Sync' variable with async exceptions masked for the 'Sync' operations, but not the action. -- Does not allow a value to be returned. -- Equivalent to 'Control.Concurrent.MVar.modifyMVar_'. modify_ ::@@ -106,7 +106,7 @@ putBlock a' {-# inline modify_ #-} --- |Modify a 'Sync' variable with async exceptions masked for the entire procedure.+-- | Modify a 'Sync' variable with async exceptions masked for the entire procedure. -- Allows a value to be returned. -- Equivalent to 'Control.Concurrent.MVar.modifyMVarMasked'. modifyMasked ::@@ -121,7 +121,7 @@ b <$ putBlock a' {-# inline modifyMasked #-} --- |Modify a 'Sync' variable with async exceptions masked for the entire procedure.+-- | Modify a 'Sync' variable with async exceptions masked for the entire procedure. -- Does not allow a value to be returned. -- Equivalent to 'Control.Concurrent.MVar.modifyMVarMasked_'. modifyMasked_ ::@@ -136,7 +136,7 @@ putBlock a' {-# inline modifyMasked_ #-} --- |Run an action with the current value of the 'Sync' variable with async exceptions masked for the 'Sync' operations,+-- | Run an action with the current value of the 'Sync' variable with async exceptions masked for the 'Sync' operations, -- but not the action. -- Equivalent to 'Control.Concurrent.MVar.withMVar'. use ::@@ -150,7 +150,7 @@ finally (restore (raise (m a))) (putBlock a) {-# inline use #-} --- |Run an action with the current value of the 'Sync' variable with async exceptions masked for the entire procedure.+-- | Run an action with the current value of the 'Sync' variable with async exceptions masked for the entire procedure. -- Equivalent to 'Control.Concurrent.MVar.withMVarMasked'. useMasked :: ∀ a b r .
lib/Polysemy/Conc/SyncRead.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: API and combinators for 'SyncRead'.+-- | Description: API and combinators for 'SyncRead'. module Polysemy.Conc.SyncRead ( module Polysemy.Conc.SyncRead, module Polysemy.Conc.Effect.SyncRead,@@ -12,7 +12,7 @@ import qualified Polysemy.Conc.Effect.SyncRead as SyncRead import Polysemy.Conc.Effect.SyncRead (SyncRead, block, empty, try, wait) --- |Run an action repeatedly until the 'SyncRead' variable is available.+-- | Run an action repeatedly until the 'SyncRead' variable is available. whileEmpty :: ∀ a r . Member (SyncRead a) r =>@@ -25,7 +25,7 @@ action whenM (not <$> SyncRead.empty @a) spin --- |Run an action repeatedly until the 'SyncRead' variable is available, waiting for the specified time between executions.+-- | Run an action repeatedly until the 'SyncRead' variable is available, waiting for the specified time between executions. whileEmptyInterval :: ∀ a u t d r . TimeUnit u =>
polysemy-conc.cabal view
@@ -1,29 +1,29 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.35.0.+-- This file has been generated from package.yaml by hpack version 0.38.2. -- -- see: https://github.com/sol/hpack name: polysemy-conc-version: 0.12.1.0+version: 0.15.0.0 synopsis: Polysemy effects for concurrency description: See https://hackage.haskell.org/package/polysemy-conc/docs/Polysemy-Conc.html category: Concurrency-homepage: https://git.tryp.io/tek/polysemy-conc+homepage: https://github.com/tek/polysemy-conc#readme bug-reports: https://github.com/tek/polysemy-conc/issues author: Torsten Schmits maintainer: hackage@tryp.io-copyright: 2022 Torsten Schmits+copyright: 2025 Torsten Schmits license: BSD-2-Clause-Patent license-file: LICENSE build-type: Simple extra-source-files:- changelog.md readme.md+ changelog.md source-repository head type: git- location: https://git.tryp.io/tek/polysemy-conc+ location: https://github.com/tek/polysemy-conc library exposed-modules:@@ -34,13 +34,11 @@ Polysemy.Conc.Effect.Critical Polysemy.Conc.Effect.Events Polysemy.Conc.Effect.Gate- Polysemy.Conc.Effect.Interrupt Polysemy.Conc.Effect.Lock Polysemy.Conc.Effect.Mask Polysemy.Conc.Effect.Monitor Polysemy.Conc.Effect.Queue Polysemy.Conc.Effect.Race- Polysemy.Conc.Effect.Scoped Polysemy.Conc.Effect.Semaphore Polysemy.Conc.Effect.Sync Polysemy.Conc.Effect.SyncRead@@ -49,7 +47,6 @@ Polysemy.Conc.Interpreter.Critical Polysemy.Conc.Interpreter.Events Polysemy.Conc.Interpreter.Gate- Polysemy.Conc.Interpreter.Interrupt Polysemy.Conc.Interpreter.Lock Polysemy.Conc.Interpreter.Mask Polysemy.Conc.Interpreter.Monitor@@ -57,7 +54,6 @@ Polysemy.Conc.Interpreter.Queue.TB Polysemy.Conc.Interpreter.Queue.TBM Polysemy.Conc.Interpreter.Race- Polysemy.Conc.Interpreter.Scoped Polysemy.Conc.Interpreter.Semaphore Polysemy.Conc.Interpreter.Stack Polysemy.Conc.Interpreter.Sync@@ -76,179 +72,123 @@ default-extensions: AllowAmbiguousTypes ApplicativeDo- BangPatterns- BinaryLiterals BlockArguments- ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass- DeriveDataTypeable- DeriveFoldable- DeriveFunctor- DeriveGeneric- DeriveLift- DeriveTraversable DerivingStrategies DerivingVia DisambiguateRecordFields- DoAndIfThenElse DuplicateRecordFields- EmptyCase- EmptyDataDecls- ExistentialQuantification- FlexibleContexts- FlexibleInstances FunctionalDependencies GADTs- GeneralizedNewtypeDeriving- InstanceSigs- KindSignatures LambdaCase LiberalTypeSynonyms- MultiParamTypeClasses+ MonadComprehensions MultiWayIf- NamedFieldPuns+ NoFieldSelectors OverloadedLabels OverloadedLists+ OverloadedRecordDot OverloadedStrings PackageImports PartialTypeSignatures- PatternGuards PatternSynonyms- PolyKinds QuantifiedConstraints QuasiQuotes- RankNTypes RecordWildCards RecursiveDo RoleAnnotations- ScopedTypeVariables- StandaloneDeriving TemplateHaskell- TupleSections- TypeApplications TypeFamilies TypeFamilyDependencies- TypeOperators- TypeSynonymInstances UndecidableInstances UnicodeSyntax ViewPatterns- ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Widentities -Wunused-packages+ ghc-options: -Wall -Widentities -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wredundant-constraints -Wunused-type-patterns -Wunused-packages build-depends:- async- , base >=4.12 && <5- , containers- , incipit-core >=0.4.1- , polysemy ==1.9.*- , polysemy-resume ==0.7.*- , polysemy-time ==0.6.*- , stm- , stm-chans >=3 && <3.1+ async >=2.2.4 && <2.3+ , base >=4.17.2.1 && <4.22+ , incipit-core >=0.4.1.0 && <0.8+ , polysemy >=1.9.0.0 && <1.10+ , polysemy-resume >=0.7.0.0 && <0.10+ , polysemy-time >=0.5.1.0 && <0.8+ , stm >=2.5.1.0 && <2.6+ , stm-chans >=2.0.0 && <3.1 , torsor ==0.1.*- , unagi-chan ==0.4.*- , unix+ , unagi-chan >=0.4.1.3 && <0.5 mixins: base hiding (Prelude) , incipit-core (IncipitCore as Prelude) , incipit-core hiding (IncipitCore)- default-language: Haskell2010+ default-language: GHC2021 -test-suite polysemy-conc-unit+test-suite polysemy-conc-test type: exitcode-stdio-1.0 main-is: Main.hs other-modules: Polysemy.Conc.Test.EventsTest- Polysemy.Conc.Test.InterruptTest Polysemy.Conc.Test.LockTest Polysemy.Conc.Test.MaskTest Polysemy.Conc.Test.MonitorTest Polysemy.Conc.Test.QueueTest+ Polysemy.Conc.Test.Run Polysemy.Conc.Test.SyncTest hs-source-dirs: test default-extensions: AllowAmbiguousTypes ApplicativeDo- BangPatterns- BinaryLiterals BlockArguments- ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass- DeriveDataTypeable- DeriveFoldable- DeriveFunctor- DeriveGeneric- DeriveLift- DeriveTraversable DerivingStrategies DerivingVia DisambiguateRecordFields- DoAndIfThenElse DuplicateRecordFields- EmptyCase- EmptyDataDecls- ExistentialQuantification- FlexibleContexts- FlexibleInstances FunctionalDependencies GADTs- GeneralizedNewtypeDeriving- InstanceSigs- KindSignatures LambdaCase LiberalTypeSynonyms- MultiParamTypeClasses+ MonadComprehensions MultiWayIf- NamedFieldPuns+ NoFieldSelectors OverloadedLabels OverloadedLists+ OverloadedRecordDot OverloadedStrings PackageImports PartialTypeSignatures- PatternGuards PatternSynonyms- PolyKinds QuantifiedConstraints QuasiQuotes- RankNTypes RecordWildCards RecursiveDo RoleAnnotations- ScopedTypeVariables- StandaloneDeriving TemplateHaskell- TupleSections- TypeApplications TypeFamilies TypeFamilyDependencies- TypeOperators- TypeSynonymInstances UndecidableInstances UnicodeSyntax ViewPatterns- ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Widentities -Wunused-packages -threaded -rtsopts -with-rtsopts=-N+ ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Widentities -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wredundant-constraints -Wunused-type-patterns -Wunused-packages build-depends:- async- , base >=4.12 && <5- , hedgehog- , incipit-core >=0.4.1- , polysemy- , polysemy-conc- , polysemy-plugin- , polysemy-resume- , polysemy-test- , polysemy-time- , stm- , tasty- , tasty-hedgehog- , time- , unix+ async >=2.2.4 && <2.3+ , base >=4.17.2.1 && <4.22+ , hedgehog >=1.4 && <1.8+ , incipit-core >=0.4.1.0 && <0.8+ , polysemy >=1.9.0.0 && <1.10+ , polysemy-conc <0.16+ , polysemy-plugin >=0.4.4.0 && <0.5+ , polysemy-test >=0.6.0.0 && <0.12+ , polysemy-time >=0.5.1.0 && <0.8+ , tasty >=1.5.2 && <1.6+ , tasty-hedgehog >=1.4.0.2 && <1.5+ , time >=1.12.2 && <1.15+ , torsor ==0.1.* mixins: base hiding (Prelude) , incipit-core (IncipitCore as Prelude) , incipit-core hiding (IncipitCore)- default-language: Haskell2010+ default-language: GHC2021
readme.md view
@@ -1,6 +1,6 @@ # About -This library provides [Polysemy] effects for using STM queues, MVars, signal handling and racing.+This library provides [Polysemy] effects for using STM queues, MVars and racing. Please visit [Hackage] for documentation.
test/Main.hs view
@@ -1,8 +1,6 @@ module Main where -import Hedgehog (property, test, withTests) import Polysemy.Conc.Test.EventsTest (test_events)-import Polysemy.Conc.Test.InterruptTest (test_interrupt) import Polysemy.Conc.Test.LockTest (test_lock) import Polysemy.Conc.Test.MaskTest (test_mask) import Polysemy.Conc.Test.MonitorTest (test_monitorBasic, test_monitorClockSkew)@@ -14,42 +12,43 @@ test_queueTBM, test_queueTimeoutTBM, )+import Polysemy.Conc.Test.Run (unitTestTimes) import Polysemy.Conc.Test.SyncTest (test_sync, test_syncLock)-import Polysemy.Test (unitTest)-import Test.Tasty (TestTree, defaultMain, testGroup)-import Test.Tasty.Hedgehog (testProperty)+import Test.Tasty (TestTree, Timeout (NoTimeout), adjustOption, defaultMain, mkTimeout, testGroup) +defaultTimeout :: Timeout -> Timeout+defaultTimeout = \case+ NoTimeout -> mkTimeout 60_000_000+ t -> t+ tests :: TestTree tests =+ adjustOption defaultTimeout $ testGroup "main" [ testGroup "queue" [- unitTest "TBM success" test_queueTBM,- unitTest "TBM timeout" test_queueTimeoutTBM,- unitTest "TBM peek" test_queuePeekTBM,- unitTest "TBM block" test_queueBlockTBM,- unitTest "TB success" test_queueTB,- unitTest "TB block" test_queueBlockTB+ unitTestTimes 100 "TBM success" test_queueTBM,+ unitTestTimes 100 "TBM timeout" test_queueTimeoutTBM,+ unitTestTimes 100 "TBM peek" test_queuePeekTBM,+ unitTestTimes 10 "TBM block" test_queueBlockTBM,+ unitTestTimes 100 "TB success" test_queueTB,+ unitTestTimes 10 "TB block" test_queueBlockTB ], testGroup "events" [- testProperty "events" (withTests 100 (property (test test_events)))+ unitTestTimes 100 "events" test_events ], testGroup "sync" [- unitTest "sync" test_sync,- unitTest "lock" test_syncLock+ unitTestTimes 100 "sync" test_sync,+ unitTestTimes 10 "lock" test_syncLock ], test_lock,- testGroup "interrupt" [- unitTest "interrupt" test_interrupt- ], testGroup "mask" [- unitTest "mask" test_mask+ unitTestTimes 10 "mask" test_mask ], testGroup "monitor" [- unitTest "basic" test_monitorBasic,- unitTest "clock skew" test_monitorClockSkew+ unitTestTimes 100 "basic" test_monitorBasic,+ unitTestTimes 100 "clock skew" test_monitorClockSkew ] ] main :: IO ()-main =- defaultMain tests+main = defaultMain tests
− test/Polysemy/Conc/Test/InterruptTest.hs
@@ -1,31 +0,0 @@-module Polysemy.Conc.Test.InterruptTest where--import Control.Concurrent.STM (TVar, atomically, modifyTVar, newTVarIO, readTVarIO)-import Polysemy.Test (UnitTest, assertEq, runTestAuto)-import System.Posix (Handler (CatchInfoOnce), SignalInfo, installHandler, keyboardSignal, raiseSignal)--import qualified Polysemy.Conc.Effect.Interrupt as Interrupt-import Polysemy.Conc.Interpreter.Critical (interpretCritical)-import Polysemy.Conc.Interpreter.Interrupt (interpretInterrupt)-import Polysemy.Conc.Interpreter.Race (interpretRace)--handler :: MVar () -> TVar Int -> SignalInfo -> IO ()-handler mv tv _ = do- atomically (modifyTVar tv (5 +))- putMVar mv ()--test_interrupt :: UnitTest-test_interrupt = do- runTestAuto do- tv <- embed (newTVarIO 0)- mv <- embed newEmptyMVar- embed (installHandler keyboardSignal (CatchInfoOnce (handler mv tv)) Nothing)- asyncToIOFinal $ interpretCritical $ interpretRace $ interpretInterrupt do- Interrupt.register "test 1" do- atomically (modifyTVar tv (3 +))- Interrupt.register "test 2" do- atomically (modifyTVar tv (9 +))- embed (raiseSignal keyboardSignal)- embed (takeMVar mv)- result <- embed (readTVarIO tv)- assertEq @_ @IO 17 result
test/Polysemy/Conc/Test/LockTest.hs view
@@ -2,7 +2,7 @@ module Polysemy.Conc.Test.LockTest where -import Polysemy.Test (UnitTest, assert, assertJust, runTestAuto, unitTest)+import Polysemy.Test (UnitTest, assert, assertJust, runTestAuto) import Polysemy.Time (GhcTime, MilliSeconds (MilliSeconds), Seconds (Seconds), interpretTimeGhc) import Test.Tasty (TestTree, testGroup) @@ -16,6 +16,7 @@ import Polysemy.Conc.Interpreter.Race (interpretRace) import Polysemy.Conc.Interpreter.Sync (interpretSync) import Polysemy.Conc.Race (timeout_)+import Polysemy.Conc.Test.Run (unitTestTimes) interpretLockTest :: Members [Resource, Embed IO, Final IO] r =>@@ -52,6 +53,6 @@ test_lock :: TestTree test_lock = testGroup "lock" [- unitTest "basic" test_lockBasic,- unitTest "reentry" test_lockReentry+ unitTestTimes 10 "basic" test_lockBasic,+ unitTestTimes 10 "reentry" test_lockReentry ]
test/Polysemy/Conc/Test/MaskTest.hs view
@@ -13,7 +13,7 @@ import Polysemy.Conc.AtomicState (interpretAtomic) import Polysemy.Conc.Effect.Mask (restore, uninterruptibleMask) import qualified Polysemy.Conc.Effect.Sync as Sync-import Polysemy.Conc.Interpreter.Mask (interpretUninterruptibleMaskFinal)+import Polysemy.Conc.Interpreter.Mask (interpretMaskFinal) import Polysemy.Conc.Interpreter.Race (interpretRace) import Polysemy.Conc.Interpreter.Sync (interpretSync) @@ -39,7 +39,7 @@ asyncToIOFinal $ interpretRace $ interpretTimeGhc $- interpretUninterruptibleMaskFinal $+ interpretMaskFinal $ interpretAtomic (0 :: Int) $ interpretSync @(Proxy 2) $ interpretSync @(Proxy 1) do
test/Polysemy/Conc/Test/MonitorTest.hs view
@@ -2,18 +2,25 @@ module Polysemy.Conc.Test.MonitorTest where +import Control.Concurrent (threadDelay)+import qualified Data.Text as Text import Data.Time (UTCTime)-import Polysemy.Test (UnitTest, assertEq, assertJust, runTestAuto)+import Polysemy.Test (TestError (TestError), UnitTest, assertEq, assertJust, runTestAuto) import qualified Polysemy.Time as Time import Polysemy.Time (+ GhcTime, Hours (Hours), MilliSeconds (MilliSeconds), Minutes (Minutes), NanoSeconds (NanoSeconds),+ Seconds (Seconds), Time,+ TimeUnit,+ convert, interpretTimeGhc, interpretTimeGhcConstantNow, )+import Torsor (difference) import Polysemy.Conc.AtomicState (interpretAtomic) import qualified Polysemy.Conc.Effect.Monitor as Monitor@@ -39,10 +46,9 @@ checker :: Members [Time t d, Sync (), Embed IO] r =>- MVar () ->- Sem r ()-checker signal =- Sync.takeBlock *> embed (putMVar signal ())+ Sem r Bool+checker =+ True <$ Sync.takeBlock test_monitorBasic :: UnitTest test_monitorBasic =@@ -55,42 +61,123 @@ interpretMonitorRestart (MonitorCheck (NanoSeconds 0) checker) do assertEq 3 =<< Monitor.restart prog +locked ::+ Members [AtomicState [Text], Error TestError, Embed IO] r =>+ Sem r a+locked = do+ embed (threadDelay 1_000_000)+ msgs <- atomicGet+ throw (TestError (Text.unlines ("Locked after:" : reverse msgs)))++takeBlock ::+ ∀ label r .+ HasCallStack =>+ Members [AtomicState [Text], Sync (Proxy label), Error TestError, Embed IO] r =>+ Text ->+ Sem r ()+takeBlock desc =+ withFrozenCallStack do+ atomicModify' (("taking '" <> desc <> "'") :)+ whenM (isNothing <$> Sync.takeWait (Seconds 1)) locked++putBlock ::+ ∀ label r .+ HasCallStack =>+ Members [AtomicState [Text], Sync (Proxy label), Error TestError, Embed IO] r =>+ Text ->+ Sem r ()+putBlock desc =+ withFrozenCallStack do+ atomicModify' (("putting '" <> desc <> "'") :)+ unlessM (Sync.putWait (Seconds 1) Proxy) locked+ progSkew ::- Member (Sync (Proxy "start")) r =>- Members [Monitor Restart, Time t d, AtomicState Int, Sync (Proxy 1), Sync (Proxy 2), Sync (Proxy 3)] r =>+ Members [Sync (Proxy "start"), Sync (Proxy "1"), Sync (Proxy "2"), Sync (Proxy "3"), Embed IO] r =>+ Members [Monitor Restart, AtomicState [Text], AtomicState Int, Error TestError, Time t d] r => Sem r Int-progSkew = do+progSkew = Monitor.monitor do atomicModify' (1 +)- Sync.putBlock (Proxy @"start")- void $ Sync.takeBlock @(Proxy 1)- Sync.putBlock (Proxy @2)- void $ Sync.takeBlock @(Proxy 3)+ putBlock @"start" "start"+ takeBlock @"1" "1"+ putBlock @"2" "2"+ takeBlock @"3" "3" atomicGet +sleepPoll ::+ Members [GhcTime, Embed IO] r =>+ TimeUnit u =>+ u ->+ UTCTime ->+ Sem r ()+sleepPoll duration start =+ spin+ where+ spin = do+ embed (threadDelay 10_000)+ unlessM (later <$> Time.now) spin+ later now =+ difference now start >= diff+ diff =+ convert duration++interceptTime ::+ ∀ r a .+ Members [AtomicState [Text], GhcTime, Sync (Proxy "sleep"), Error TestError, Embed IO] r =>+ Sem r a ->+ Sem r a+interceptTime =+ intercept \case+ Time.Now -> do+ atomicModify' ("now" :)+ Time.now+ Time.Today ->+ Time.today+ Time.Sleep t -> do+ atomicModify' ("sleep" :)+ now <- Time.now+ putBlock @"sleep" "sleep"+ sleepPoll t now+ atomicModify' ("slept" :)+ Time.SetTime now ->+ Time.setTime now+ Time.Adjust (diff :: u) -> do+ atomicModify' ("adjust" :)+ send (Time.Adjust diff)+ Time.SetDate startAt ->+ Time.setDate startAt+ test_monitorClockSkew :: UnitTest test_monitorClockSkew = runTestAuto $ asyncToIOFinal $ interpretRace $- interpretTimeGhcConstantNow $ interpretAtomic (0 :: Int) $+ interpretAtomic ([] :: [Text]) $ interpretAtomic @(Maybe UTCTime) Nothing $ interpretSync @(Proxy "start") $- interpretSync @(Proxy 1) $- interpretSync @(Proxy 2) $- interpretSync @(Proxy 3) $- interpretMonitorRestart (monitorClockSkew (clockSkewConfig (MilliSeconds 1) (Minutes 30))) do+ interpretSync @(Proxy "sleep") $+ interpretSync @(Proxy "1") $+ interpretSync @(Proxy "2") $+ interpretSync @(Proxy "3") $+ interpretTimeGhcConstantNow $+ interceptTime $+ interpretMonitorRestart monitor do h <- async (Monitor.restart progSkew)- _ <- Sync.takeBlock @(Proxy "start")+ takeBlock @"start" "start 1"+ takeBlock @"sleep" "sleep" Time.adjust (Hours 1)- _ <- Sync.takeBlock @(Proxy "start")+ _ <- takeBlock @"start" "start 2"+ takeBlock @"sleep" "sleep" Time.adjust (Minutes 10)- Sync.putBlock (Proxy @1)- Sync.takeBlock @(Proxy 2)+ putBlock @"1" "1 1"+ takeBlock @"2" "2 1"+ takeBlock @"sleep" "sleep" Time.adjust (Hours 1)- _ <- Sync.takeBlock @(Proxy "start")- Sync.putBlock (Proxy @1)- Sync.takeBlock @(Proxy 2)- Sync.putBlock (Proxy @3)+ _ <- takeBlock @"start" "start 3"+ putBlock @"1" "1 2"+ takeBlock @"2" "2 2"+ putBlock @"3" "3" assertJust 3 =<< await h+ where+ monitor = monitorClockSkew (clockSkewConfig (MilliSeconds 1) (Minutes 30))
+ test/Polysemy/Conc/Test/Run.hs view
@@ -0,0 +1,14 @@+module Polysemy.Conc.Test.Run where++import Hedgehog (TestLimit, property, test, withTests)+import Polysemy.Test (UnitTest)+import Test.Tasty (TestName, TestTree)+import Test.Tasty.Hedgehog (testProperty)++unitTestTimes ::+ TestLimit ->+ TestName ->+ UnitTest ->+ TestTree+unitTestTimes n desc =+ testProperty desc . withTests n . property . test