module Polysemy.Final.More where

import Polysemy
import Polysemy.Internal
import Polysemy.Internal.Union
import Polysemy.Final

-- | Run a @'Final' ('Sem' r)@ effect if the remaining effect stack is @r@.
-- This is sometimes useful for interpreters that make use of
-- 'reinterpret', 'raiseUnder', or any of their friends.
runFinalSem :: Sem (Final (Sem r) ': r) a -> Sem r a
runFinalSem = usingSem $ \u -> case decomp u of
  Right (Weaving (WithWeavingToFinal wav) s wv ex ins) ->
    ex <$> wav s (runFinalSem . wv) ins
  Left g -> liftSem (hoist runFinalSem g)
{-# INLINE runFinalSem #-}

-- | Run a @'Final' m@ effect by providing an explicit lowering function.
-- /Beware/: The lowering function may be invoked multiple times, so
-- __don't do any initialization work inside the lowering function__:
-- it will be duplicated.
lowerFinal :: Member (Embed m) r
           => (forall x. Sem r x -> m x)
           -> Sem (Final m ': r) a
           -> Sem r a
-- TODO(KingoftheHomeless): Write everything out for efficiency?
lowerFinal f = runFinalSem . finalToFinal embed f . raiseUnder
{-# INLINE lowerFinal #-}