{-# LANGUAGE UnboxedTuples #-}
{-# OPTIONS_HADDOCK not-home #-}
-- |
-- Copyright: (c) 2021 Xy Ren
-- License: BSD3
-- Maintainer: xy.r@outlook.com
-- Stability: unstable
-- Portability: non-portable (GHC only)
--
-- This module contains the definition of the 'Eff' monad. Most of the times, you won't need to use this module
-- directly; user-facing functionalities are all exported via the "Cleff" module.
--
-- __This is an /internal/ module and its API may change even between minor versions.__ Therefore you should be
-- extra careful if you're to depend on this module.
module Cleff.Internal.Monad
  ( -- * The 'Eff' monad
    InternalHandler (InternalHandler, runHandler), Eff (Eff, unEff)
  , -- * Effect environment
    Env, HandlerPtr, emptyEnv, adjustEnv, allocaEnv, readEnv, writeEnv, replaceEnv, appendEnv, updateEnv
  , -- * Performing effect operations
    KnownList, Subset, send, sendVia
  ) where

import           Cleff.Internal.Any
import           Cleff.Internal.Effect
import           Control.Applicative   (Applicative (liftA2))
import           Control.Monad.Fix     (MonadFix (mfix))
import           Data.IntMap.Strict    (IntMap)
import qualified Data.IntMap.Strict    as Map
import           Data.Rec.SmallArray   (KnownList, Rec, Subset, pattern (:~:))
import qualified Data.Rec.SmallArray   as Rec

-- * The 'Eff' monad

-- | The internal representation of effect handlers. This is just a natural transformation from the effect type
-- @e ('Eff' es)@ to the effect monad @'Eff' es@ for any effect stack @es@.
--
-- In interpreting functions (see "Cleff.Internal.Interpret"), the user-facing 'Cleff.Handler' type is transformed into
-- this type.
newtype InternalHandler e = InternalHandler
  { InternalHandler e
-> forall (es :: [Effect]) a. e (Eff es) a -> Eff es a
runHandler ::  es. e (Eff es) ~> Eff es }

-- | The extensible effect monad. A monad @'Eff' es@ is capable of performing any effect in the /effect stack/ @es@,
-- which is a type-level list that holds all effects available. However, most of the times, for flexibility, @es@
-- should be a polymorphic type variable, and you should use the '(:>)' and '(:>>)' operators in constraints to
-- indicate what effects are in the stack. For example,
--
-- @
-- 'Cleff.Reader.Reader' 'String' ':>' es, 'Cleff.State.State' 'Bool' ':>' es => 'Eff' es 'Integer'
-- @
--
-- allows you to perform operations of the @'Cleff.Reader.Reader' 'String'@ effect and the @'Cleff.State.State' 'Bool'@
-- effect in a computation returning an 'Integer'.
type role Eff nominal representational
newtype Eff es a = Eff { Eff es a -> Env es -> IO a
unEff :: Env es -> IO a }
  -- ^ The effect monad receives an effect environment 'Env' that contains all effect handlers and produces an 'IO'
  -- action.

instance Functor (Eff es) where
  fmap :: (a -> b) -> Eff es a -> Eff es b
fmap a -> b
f (Eff Env es -> IO a
x) = (Env es -> IO b) -> Eff es b
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff ((a -> b) -> IO a -> IO b
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f (IO a -> IO b) -> (Env es -> IO a) -> Env es -> IO b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Env es -> IO a
x)
  {-# INLINE fmap #-}
  a
x <$ :: a -> Eff es b -> Eff es a
<$ Eff Env es -> IO b
y = (Env es -> IO a) -> Eff es a
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es
es -> a
x a -> IO b -> IO a
forall (f :: Type -> Type) a b. Functor f => a -> f b -> f a
<$ Env es -> IO b
y Env es
es
  {-# INLINE (<$) #-}

instance Applicative (Eff es) where
  pure :: a -> Eff es a
pure = (Env es -> IO a) -> Eff es a
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff ((Env es -> IO a) -> Eff es a)
-> (a -> Env es -> IO a) -> a -> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO a -> Env es -> IO a
forall a b. a -> b -> a
const (IO a -> Env es -> IO a) -> (a -> IO a) -> a -> Env es -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> IO a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure
  {-# INLINE pure #-}
  Eff Env es -> IO (a -> b)
f <*> :: Eff es (a -> b) -> Eff es a -> Eff es b
<*> Eff Env es -> IO a
x = (Env es -> IO b) -> Eff es b
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es
es -> Env es -> IO (a -> b)
f Env es
es IO (a -> b) -> IO a -> IO b
forall (f :: Type -> Type) a b.
Applicative f =>
f (a -> b) -> f a -> f b
<*> Env es -> IO a
x Env es
es
  {-# INLINE (<*>) #-}
  Eff Env es -> IO a
x <* :: Eff es a -> Eff es b -> Eff es a
<*  Eff Env es -> IO b
y = (Env es -> IO a) -> Eff es a
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es
es -> Env es -> IO a
x Env es
es IO a -> IO b -> IO a
forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f a
<*  Env es -> IO b
y Env es
es
  {-# INLINE (<*) #-}
  Eff Env es -> IO a
x  *> :: Eff es a -> Eff es b -> Eff es b
*> Eff Env es -> IO b
y = (Env es -> IO b) -> Eff es b
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es
es -> Env es -> IO a
x Env es
es  IO a -> IO b -> IO b
forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f b
*> Env es -> IO b
y Env es
es
  {-# INLINE (*>) #-}
  liftA2 :: (a -> b -> c) -> Eff es a -> Eff es b -> Eff es c
liftA2 a -> b -> c
f (Eff Env es -> IO a
x) (Eff Env es -> IO b
y) = (Env es -> IO c) -> Eff es c
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es
es -> (a -> b -> c) -> IO a -> IO b -> IO c
forall (f :: Type -> Type) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 a -> b -> c
f (Env es -> IO a
x Env es
es) (Env es -> IO b
y Env es
es)
  {-# INLINE liftA2 #-}

instance Monad (Eff es) where
  Eff Env es -> IO a
x >>= :: Eff es a -> (a -> Eff es b) -> Eff es b
>>= a -> Eff es b
f = (Env es -> IO b) -> Eff es b
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es
es -> Env es -> IO a
x Env es
es IO a -> (a -> IO b) -> IO b
forall (m :: Type -> Type) a b. Monad m => m a -> (a -> m b) -> m b
>>= \a
x' -> Eff es b -> Env es -> IO b
forall (es :: [Effect]) a. Eff es a -> Env es -> IO a
unEff (a -> Eff es b
f a
x') Env es
es
  {-# INLINE (>>=) #-}
  >> :: Eff es a -> Eff es b -> Eff es b
(>>) = Eff es a -> Eff es b -> Eff es b
forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f b
(*>)
  {-# INLINE (>>) #-}

instance MonadFix (Eff es) where
  mfix :: (a -> Eff es a) -> Eff es a
mfix a -> Eff es a
f = (Env es -> IO a) -> Eff es a
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es
es -> (a -> IO a) -> IO a
forall (m :: Type -> Type) a. MonadFix m => (a -> m a) -> m a
mfix \a
x -> Eff es a -> Env es -> IO a
forall (es :: [Effect]) a. Eff es a -> Env es -> IO a
unEff (a -> Eff es a
f a
x) Env es
es
  {-# INLINE mfix #-}

-- * Effect environment

-- | The /effect environment/ that corresponds effects in the stack to their respective 'InternalHandler's. This
-- structure simulates memory: handlers are retrieved via pointers ('HandlerPtr's), and for each effect in the stack
-- we can either change what pointer it uses or change the handler the pointer points to. The former is used for global
-- effect interpretation ('Cleff.reinterpretN') and the latter for local interpretation ('Cleff.toEffWith') in order to
-- retain correct HO semantics. For more details on this see https://github.com/re-xyr/cleff/issues/5.
type role Env nominal
data Env (es :: [Effect]) = Env
  {-# UNPACK #-} !(Rec HandlerPtr es) -- ^ The array.
  {-# UNPACK #-} !Int -- ^ The next memory address to allocate.
  !(IntMap Any) -- ^ The simulated memory.

-- | A pointer to 'InternalHandler' in an 'Env'.
type role HandlerPtr nominal
newtype HandlerPtr (e :: Effect) = HandlerPtr { HandlerPtr e -> Int
unHandlerPtr :: Int }

-- | Create an empty 'Env' with no address allocated.
emptyEnv :: Env '[]
emptyEnv :: Env '[]
emptyEnv = Rec HandlerPtr '[] -> Int -> IntMap Any -> Env '[]
forall (es :: [Effect]).
Rec HandlerPtr es -> Int -> IntMap Any -> Env es
Env Rec HandlerPtr '[]
forall k (f :: k -> Type). Rec f '[]
Rec.empty Int
0 IntMap Any
forall a. IntMap a
Map.empty
{-# INLINE emptyEnv #-}

-- | Adjust the effect stack via an function over 'Rec'.
adjustEnv ::  es' es. (Rec HandlerPtr es -> Rec HandlerPtr es') -> Env es -> Env es'
adjustEnv :: (Rec HandlerPtr es -> Rec HandlerPtr es') -> Env es -> Env es'
adjustEnv Rec HandlerPtr es -> Rec HandlerPtr es'
f (Env Rec HandlerPtr es
re Int
n IntMap Any
mem) = Rec HandlerPtr es' -> Int -> IntMap Any -> Env es'
forall (es :: [Effect]).
Rec HandlerPtr es -> Int -> IntMap Any -> Env es
Env (Rec HandlerPtr es -> Rec HandlerPtr es'
f Rec HandlerPtr es
re) Int
n IntMap Any
mem
{-# INLINE adjustEnv #-}

-- | Allocate a new, empty address for a handler. \( O(1) \).
allocaEnv ::  e es. Env es -> (# HandlerPtr e, Env es #)
allocaEnv :: Env es -> (# HandlerPtr e, Env es #)
allocaEnv (Env Rec HandlerPtr es
re Int
n IntMap Any
mem) = (# Int -> HandlerPtr e
forall (e :: Effect). Int -> HandlerPtr e
HandlerPtr Int
n, Rec HandlerPtr es -> Int -> IntMap Any -> Env es
forall (es :: [Effect]).
Rec HandlerPtr es -> Int -> IntMap Any -> Env es
Env Rec HandlerPtr es
re (Int -> Int
forall a. Enum a => a -> a
succ Int
n) IntMap Any
mem #)
{-# INLINE allocaEnv #-}

-- | Read the handler a pointer points to. \( O(1) \).
readEnv ::  e es. Rec.Elem e es => Env es -> InternalHandler e
readEnv :: Env es -> InternalHandler e
readEnv (Env Rec HandlerPtr es
re Int
_ IntMap Any
mem) = Any -> InternalHandler e
forall a. Any -> a
fromAny (Any -> InternalHandler e) -> Any -> InternalHandler e
forall a b. (a -> b) -> a -> b
$ IntMap Any
mem IntMap Any -> Int -> Any
forall a. IntMap a -> Int -> a
Map.! HandlerPtr e -> Int
forall (e :: Effect). HandlerPtr e -> Int
unHandlerPtr (Rec HandlerPtr es -> HandlerPtr e
forall k (e :: k) (es :: [k]) (f :: k -> Type).
Elem e es =>
Rec f es -> f e
Rec.index @e Rec HandlerPtr es
re)
{-# INLINE readEnv #-}

-- | Overwrite the handler a pointer points to. \( O(1) \).
writeEnv ::  e es. HandlerPtr e -> InternalHandler e -> Env es -> Env es
writeEnv :: HandlerPtr e -> InternalHandler e -> Env es -> Env es
writeEnv (HandlerPtr Int
m) InternalHandler e
x (Env Rec HandlerPtr es
re Int
n IntMap Any
mem) = Rec HandlerPtr es -> Int -> IntMap Any -> Env es
forall (es :: [Effect]).
Rec HandlerPtr es -> Int -> IntMap Any -> Env es
Env Rec HandlerPtr es
re Int
n (Int -> Any -> IntMap Any -> IntMap Any
forall a. Int -> a -> IntMap a -> IntMap a
Map.insert Int
m (InternalHandler e -> Any
forall a. a -> Any
toAny InternalHandler e
x) IntMap Any
mem)
{-# INLINE writeEnv #-}

-- | Replace the handler pointer of an effect in the stack. \( O(n) \).
replaceEnv ::  e es. Rec.Elem e es => HandlerPtr e -> InternalHandler e -> Env es -> Env es
replaceEnv :: HandlerPtr e -> InternalHandler e -> Env es -> Env es
replaceEnv (HandlerPtr Int
m) InternalHandler e
x (Env Rec HandlerPtr es
re Int
n IntMap Any
mem) = Rec HandlerPtr es -> Int -> IntMap Any -> Env es
forall (es :: [Effect]).
Rec HandlerPtr es -> Int -> IntMap Any -> Env es
Env (HandlerPtr e -> Rec HandlerPtr es -> Rec HandlerPtr es
forall k (e :: k) (es :: [k]) (f :: k -> Type).
Elem e es =>
f e -> Rec f es -> Rec f es
Rec.update @e (Int -> HandlerPtr e
forall (e :: Effect). Int -> HandlerPtr e
HandlerPtr Int
m) Rec HandlerPtr es
re) Int
n (Int -> Any -> IntMap Any -> IntMap Any
forall a. Int -> a -> IntMap a -> IntMap a
Map.insert Int
m (InternalHandler e -> Any
forall a. a -> Any
toAny InternalHandler e
x) IntMap Any
mem)
{-# INLINE replaceEnv #-}

-- | Add a new effect to the stack with its corresponding handler pointer. \( O(n) \).
appendEnv ::  e es. HandlerPtr e -> InternalHandler e -> Env es -> Env (e ': es)
appendEnv :: HandlerPtr e -> InternalHandler e -> Env es -> Env (e : es)
appendEnv (HandlerPtr Int
m) InternalHandler e
x (Env Rec HandlerPtr es
re Int
n IntMap Any
mem) = Rec HandlerPtr (e : es) -> Int -> IntMap Any -> Env (e : es)
forall (es :: [Effect]).
Rec HandlerPtr es -> Int -> IntMap Any -> Env es
Env (Int -> HandlerPtr e
forall (e :: Effect). Int -> HandlerPtr e
HandlerPtr Int
m HandlerPtr e -> Rec HandlerPtr es -> Rec HandlerPtr (e : es)
forall a (f :: a -> Type) (e :: a) (es :: [a]).
f e -> Rec f es -> Rec f (e : es)
:~: Rec HandlerPtr es
re) Int
n (Int -> Any -> IntMap Any -> IntMap Any
forall a. Int -> a -> IntMap a -> IntMap a
Map.insert Int
m (InternalHandler e -> Any
forall a. a -> Any
toAny InternalHandler e
x) IntMap Any
mem)
{-# INLINE appendEnv #-}

-- | Use the state of LHS as a newer version for RHS. \( O(1) \).
updateEnv ::  es es'. Env es' -> Env es -> Env es
updateEnv :: Env es' -> Env es -> Env es
updateEnv (Env Rec HandlerPtr es'
_ Int
n IntMap Any
mem) (Env Rec HandlerPtr es
re' Int
_ IntMap Any
_) = Rec HandlerPtr es -> Int -> IntMap Any -> Env es
forall (es :: [Effect]).
Rec HandlerPtr es -> Int -> IntMap Any -> Env es
Env Rec HandlerPtr es
re' Int
n IntMap Any
mem
{-# INLINE updateEnv #-}

-- * Performing effect operations

-- | Perform an effect operation, /i.e./ a value of an effect type @e :: 'Effect'@. This requires @e@ to be in the
-- effect stack.
send :: e :> es => e (Eff es) ~> Eff es
send :: e (Eff es) ~> Eff es
send = (Eff es ~> Eff es) -> e (Eff es) ~> Eff es
forall (e :: Effect) (es' :: [Effect]) (es :: [Effect]).
(e :> es') =>
(Eff es ~> Eff es') -> e (Eff es) ~> Eff es'
sendVia forall a. a -> a
Eff es ~> Eff es
id

-- | Perform an action in another effect stack via a transformation to that stack; in other words, this function "maps"
-- the effect operation from effect stack @es@ to @es'@. This is a generalization of 'send'; end users most likely
-- won't need to use this.
--
-- @
-- 'send' = 'sendVia' 'id'
-- @
--
-- @since 0.2.0.0
sendVia :: e :> es' => (Eff es ~> Eff es') -> e (Eff es) ~> Eff es'
sendVia :: (Eff es ~> Eff es') -> e (Eff es) ~> Eff es'
sendVia Eff es ~> Eff es'
f e (Eff es) a
e = (Env es' -> IO a) -> Eff es' a
forall (es :: [Effect]) a. (Env es -> IO a) -> Eff es a
Eff \Env es'
es -> Eff es' a -> Env es' -> IO a
forall (es :: [Effect]) a. Eff es a -> Env es -> IO a
unEff (Eff es a -> Eff es' a
Eff es ~> Eff es'
f (InternalHandler e -> e (Eff es) a -> Eff es a
forall (e :: Effect).
InternalHandler e
-> forall (es :: [Effect]) a. e (Eff es) a -> Eff es a
runHandler (Env es' -> InternalHandler e
forall (e :: Effect) (es :: [Effect]).
Elem e es =>
Env es -> InternalHandler e
readEnv Env es'
es) e (Eff es) a
e)) Env es'
es