content: {-# OPTIONS_HADDOCK not-home #-}
  
  -- | Description: The auxiliary effect 'Strategy' used for building interpreters
  -- that embed 'Sem's in 'IO' callbacks
  module Polysemy.Internal.Strategy where
  
  import Polysemy.Internal
  import Polysemy.Internal.Combinators
  import Polysemy.Internal.Tactics (Inspector(..))
  
  
  ------------------------------------------------------------------------------
  -- | See 'Strategic'.
  data Strategy m f n z a where
    GetInitialState     :: Strategy m f n z (f ())
    HoistInterpretation :: (a -> n b) -> Strategy m f n z (f a -> m (f b))
    GetInspector        :: Strategy m f n z (Inspector f)
  
  
  ------------------------------------------------------------------------------
  -- | 'Strategic' is an environment in which you're capable of explicitly
  -- threading higher-order effect states to the final monad.
  -- This is a variant of @Tactics@ (see 'Polysemy.Tactical'), and usage
  -- is extremely similar.
  --
  -- @since 1.2.0.0
  type Strategic m n a = forall f. Functor f => Sem (WithStrategy m f n) (m (f a))
  
  
  ------------------------------------------------------------------------------
  -- | @since 1.2.0.0
  type WithStrategy m f n = '[Strategy m f n]
  
  
  ------------------------------------------------------------------------------
  -- | Internal function to process Strategies in terms of
  -- 'Polysemy.Final.withWeavingToFinal'.
  --
  -- @since 1.2.0.0
  runStrategy :: Functor f
              => Sem '[Strategy m f n] a
              -> f ()
              -> (forall x. f (n x) -> m (f x))
              -> (forall x. f x -> Maybe x)
              -> a
  runStrategy sem = \s wv ins -> run $ interpret
    (\case
      GetInitialState       -> pure s
      HoistInterpretation f -> pure $ \fa -> wv (f <$> fa)
      GetInspector          -> pure (Inspector ins)
    ) sem
  {-# INLINE runStrategy #-}
  
  
  ------------------------------------------------------------------------------
  -- | Get a natural transformation capable of potentially inspecting values
  -- inside of @f@. Binding the result of 'getInspectorS' produces a function that
  -- can sometimes peek inside values returned by 'bindS'.
  --
  -- This is often useful for running callback functions that are not managed by
  -- polysemy code.
  --
  -- See also 'Polysemy.getInspectorT'
  --
  -- @since 1.2.0.0
  getInspectorS :: forall m f n. Sem (WithStrategy m f n) (Inspector f)
  getInspectorS = send (GetInspector @m @f @n)
  {-# INLINE getInspectorS #-}
  
  
  ------------------------------------------------------------------------------
  -- | Get the stateful environment of the world at the moment the
  -- @Strategy@ is to be run.
  --
  -- Prefer 'pureS', 'liftS', 'runS', or 'bindS' instead of using this function
  -- directly.
  --
  -- @since 1.2.0.0
  getInitialStateS :: forall m f n. Sem (WithStrategy m f n) (f ())
  getInitialStateS = send (GetInitialState @m @f @n)
  {-# INLINE getInitialStateS #-}
  
  
  ------------------------------------------------------------------------------
  -- | Embed a value into 'Strategic'.
  --
  -- @since 1.2.0.0
  pureS :: Applicative m => a -> Strategic m n a
  pureS a = pure . (a <$) <$> getInitialStateS
  {-# INLINE pureS #-}
  
  
  ------------------------------------------------------------------------------
  -- | Lifts an action of the final monad into 'Strategic'.
  --
  -- /Note/: you don't need to use this function if you already have a monadic
  -- action with the functorial state threaded into it, by the use of
  -- 'runS' or 'bindS'.
  -- In these cases, you need only use 'pure' to embed the action into the
  -- 'Strategic' environment.
  --
  -- @since 1.2.0.0
  liftS :: Functor m => m a -> Strategic m n a
  liftS m = do
    s <- getInitialStateS
    pure $ (<$ s) <$> m
  {-# INLINE liftS #-}
  
  
  ------------------------------------------------------------------------------
  -- | Lifts a monadic action into the stateful environment, in terms
  -- of the final monad.
  -- The stateful environment will be the same as the one that the @Strategy@
  -- is initially run in.
  --
  -- Use 'bindS'  if you'd prefer to explicitly manage your stateful environment.
  --
  -- @since 1.2.0.0
  runS :: n a -> Sem (WithStrategy m f n) (m (f a))
  runS na = bindS (const na) <*> getInitialStateS
  {-# INLINE runS #-}
  
  
  ------------------------------------------------------------------------------
  -- | Embed a kleisli action into the stateful environment, in terms of the final
  -- monad. You can use 'bindS' to get an effect parameter of the form @a -> n b@
  -- into something that can be used after calling 'runS' on an effect parameter
  -- @n a@.
  --
  -- @since 1.2.0.0
  bindS :: (a -> n b) -> Sem (WithStrategy m f n) (f a -> m (f b))
  bindS = send . HoistInterpretation
  {-# INLINE bindS #-}
  