{-# LANGUAGE RankNTypes #-}

{- | Provides a mechanism to kick off the evaluation of an effect stack that takes place in a monadic context.

'Lift' effects are always the last effect in a given effect stack. These stacks are invoked with 'Control.Carrier.Lift.runM' or 'Control.Algebra.run'.

Predefined carriers:

* "Control.Carrier.Lift"
* 'IO'
* 'Data.Functor.Identity.Identity'

@since 0.1.0.0
-}

module Control.Effect.Lift
( -- * Lift effect
  Lift(..)
, sendM
, sendIO
, liftWith
  -- * Re-exports
, Algebra
, Has
, run
) where

import Control.Algebra
import Control.Effect.Lift.Internal (Lift(..))

-- | Given a @Lift n@ constraint in a signature carried by @m@, 'sendM'
-- promotes arbitrary actions of type @n a@ to @m a@. It is spiritually
-- similar to @lift@ from the @MonadTrans@ typeclass.
--
-- @since 1.0.0.0
sendM :: (Has (Lift n) sig m, Functor n) => n a -> m a
sendM :: forall (n :: * -> *) (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
(Has (Lift n) sig m, Functor n) =>
n a -> m a
sendM n a
m = forall (n :: * -> *) (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
Has (Lift n) sig m =>
(forall (ctx :: * -> *).
 Functor ctx =>
 Handler ctx m n -> ctx () -> n (ctx a))
-> m a
liftWith (\ Handler ctx m n
_ ctx ()
ctx -> (forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ctx ()
ctx) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> n a
m)
{-# INLINE sendM #-}

-- | A type-restricted variant of 'sendM' for 'IO' actions.
--
-- This is particularly useful when you have a @'Has' ('Lift' 'IO') sig m@ constraint for the use of 'liftWith', and want to run an action abstracted over 'Control.Monad.IO.Class.MonadIO'. 'IO' has a 'Control.Monad.IO.Class.MonadIO' instance, and 'sendIO'’s type restricts the action’s type to 'IO' without further type annotations.
--
-- @since 1.0.2.0
sendIO :: Has (Lift IO) sig m => IO a -> m a
sendIO :: forall (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
Has (Lift IO) sig m =>
IO a -> m a
sendIO = forall (n :: * -> *) (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
(Has (Lift n) sig m, Functor n) =>
n a -> m a
sendM
{-# INLINE sendIO #-}


-- | Run actions in an outer context.
--
-- This can be used to provide interoperation with @base@ functionality like @"Control.Exception".'Control.Exception.catch'@:
--
-- @
-- 'liftWith' $ \\ hdl ctx -> 'Control.Exception.catch' (hdl (m <$ ctx)) (hdl . (<$ ctx) . h)
-- @
--
-- The higher-order function takes both an initial context, and a handler phrased as a distributive law (as described in the documentation for 'Handler'). This handler takes actions lifted into a context functor, which can be either the initial context, or the derived context produced by handling a previous action.
--
-- As with @MonadBaseControl@, care must be taken when lifting functions like @"Control.Exception".'Control.Exception.finally'@ which don’t use the return value of one of their actions, as this can lead to dropped effects.
--
-- @since 1.0.0.0
liftWith
  :: Has (Lift n) sig m
  => (forall ctx . Functor ctx => Handler ctx m n -> ctx () -> n (ctx a))
  -> m a
liftWith :: forall (n :: * -> *) (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
Has (Lift n) sig m =>
(forall (ctx :: * -> *).
 Functor ctx =>
 Handler ctx m n -> ctx () -> n (ctx a))
-> m a
liftWith forall (ctx :: * -> *).
Functor ctx =>
Handler ctx m n -> ctx () -> n (ctx a)
with = forall (eff :: (* -> *) -> * -> *) (sig :: (* -> *) -> * -> *)
       (m :: * -> *) a.
(Member eff sig, Algebra sig m) =>
eff m a -> m a
send (forall (m :: * -> *) (sig :: * -> *) a.
(forall (ctx :: * -> *).
 Functor ctx =>
 Handler ctx m sig -> ctx () -> sig (ctx a))
-> Lift sig m a
LiftWith forall (ctx :: * -> *).
Functor ctx =>
Handler ctx m n -> ctx () -> n (ctx a)
with)
{-# INLINE liftWith #-}