module Polysemy.Final.Async
  (
    module Polysemy.Async
  , module Polysemy.Final
  , runAsyncFinal
  ) where

import qualified Control.Concurrent.Async as A

import Polysemy
import Polysemy.Async
import Polysemy.Final

------------------------------------------------------------------------------
-- | Run an 'Async' effect through final 'IO'
--
-- This can be used as an alternative to 'lowerAsync'.
--
-- /Beware/: Effects that aren't interpreted in terms of 'IO'
-- will have local state semantics in regards to 'Async' effects
-- interpreted this way. See 'interpretFinal'.
--
-- Notably, unlike 'asyncToIO', this is not consistent with
-- 'Polysemy.State.State' unless 'Polysemy.State.runStateInIORef' is used.
-- State that seems like it should be threaded globally throughout the `Async`
-- /will not be./
--
-- Prefer 'asyncToIO' unless its unsafe or inefficient in the context of your
-- application.
runAsyncFinal :: Member (Final IO) r
              => Sem (Async ': r) a
              -> Sem r a
runAsyncFinal = interpretFinal $ \case
  Async m -> do
    ins <- getInspectorS
    m' <- runS m
    liftS $ A.async (inspect ins <$> m')
  Await a -> liftS (A.wait a)