module Control.Effect.Cont ( -- * Effects Cont(..) , Shift(..) -- * Actions , callCC , shift -- * Interpretations , runCont , runShift , contToShift -- * Threading constraints , ContThreads -- * Carriers , ContC , ShiftC , ContToShiftC ) where import Data.Coerce import Control.Effect import Control.Effect.Internal.Cont import Control.Effect.Internal.Utils import Control.Monad.Trans.Free.Church.Alternate -- | Call with current continuation. The argument computation is provided -- the /continuation/ of the program at the point that 'callCC' was invoked. -- If the continuation is executed, then control will immediately abort -- and jump to the point 'callCC' was invoked, which will then return -- the argument provided to the continuation. -- -- The way higher-order actions interact with the continuation depends -- on the interpretation of 'Cont'. In general, you cannot expect to interact -- with the continuation in any meaningful way: for example, you should not -- assume that you will be able to catch an exception thrown at some point in -- the future of the computation by using 'Control.Effect.Error.catch' on the -- continuation. callCC :: Eff Cont m => ((forall b. a -> m b) -> m a) -> m a callCC main = send (CallCC main) {-# INLINE callCC #-} -- | Non-abortive call with current continuation. The argument computation is -- provided the /continuation/ of the program at the point that 'shift' was invoked. -- If the continuation is executed, then control will jump to the point 'shift' -- was invoked, which will then return the argument provided to the continuation. -- -- Once the program finishes, and produces an @r@, control will jump /back/ -- to where the continuation was executed, and return that @r@. -- From that point, you may decide whether or not to modify the final @r@, -- or invoke the continuation again with a different argument. -- -- You can also use 'shift' to abort the execution of the program early -- by simply not executing the provided continuation, and instead -- provide the final @r@ directly. -- -- The way higher-order actions interact with the continuation depends -- on the interpretation of 'Shift'. In general, you cannot expect to interact -- with the continuation in any meaningful way: for example, you should not -- assume that you will be able to catch an exception thrown at some point in -- the future of the computation by using 'Control.Effect.Error.catch' on the -- continuation. shift :: Eff (Shift r) m => ((a -> m r) -> m r) -> m a shift = send .# Shift {-# INLINE shift #-} -- | Run a 'Cont' effect. -- -- @'Derivs' ('ContC' r m) = 'Cont' ': 'Derivs' m@ -- -- @'Control.Effect.Primitive.Prims' ('ContC' r m) = 'Control.Effect.Primitive.Prims' m@ runCont :: forall a m p . ( Carrier m , Threaders '[ContThreads] m p ) => ContC a m a -> m a runCont = foldFreeT id (\c -> \case Exit a -> pure a Attempt m -> m >>= c GetCont -> c $ Left (c . Right) ) .# unContC {-# INLINE runCont #-} -- | Run a @'Shift' r@ effect if the program returns @r@. -- -- @'Derivs' ('ShiftC' r m) = 'Shift' r ': 'Derivs' m@ -- -- @'Control.Effect.Primitive.Prims' ('ShiftC' r m) = 'Control.Effect.Primitive.Prims' m@ runShift :: forall r m p . ( Carrier m , Threaders '[ContThreads] m p ) => ShiftC r m r -> m r runShift = coerce (runCont @r @m @p) {-# INLINE runShift #-} data ContToShiftH r instance Eff (Shift r) m => Handler (ContToShiftH r) Cont m where effHandler = \case CallCC main -> shift @r $ \c -> main (\a -> shift $ \_ -> c a) >>= c {-# INLINEABLE effHandler #-} type ContToShiftC r = InterpretC (ContToShiftH r) Cont -- | Transform a 'Cont' effect into a @'Shift' r@ effect. contToShift :: Eff (Shift r) m => ContToShiftC r m a -> m a contToShift = interpretViaHandler {-# INLINE contToShift #-}