extensible-effects-concurrent-2.0.0: src/Control/Eff/Concurrent/Protocol/CallbackServer.hs
{-# LANGUAGE UndecidableInstances #-}
-- | Build a "Control.Eff.Concurrent.EffectfulServer" from callbacks.
--
-- This module contains in instance of 'E.Server' that delegates to
-- callback functions.
--
-- @since 0.27.0
module Control.Eff.Concurrent.Protocol.CallbackServer
( start,
startLink,
Server,
ServerId (..),
Event (..),
TangibleCallbacks,
Callbacks,
callbacks,
onEvent,
CallbacksEff,
callbacksEff,
onEventEff,
)
where
import Control.DeepSeq
import Control.Eff
import Control.Eff.Concurrent.Process
import Control.Eff.Concurrent.Protocol
import Control.Eff.Concurrent.Protocol.EffectfulServer (Event (..))
import qualified Control.Eff.Concurrent.Protocol.EffectfulServer as E
import Control.Eff.Extend ()
import Control.Eff.Log
import Data.Coerce
import Data.Kind
import Data.Proxy
import Data.String
import qualified Data.Text as T
import Data.Typeable
-- | Execute the server loop, that dispatches incoming events
-- to either a set of 'Callbacks' or 'CallbacksEff'.
--
-- @since 0.29.1
start ::
forall (tag :: Type) eLoop q e.
( TangibleCallbacks tag eLoop q,
E.Server (Server tag eLoop q) (Processes q),
FilteredLogging (Processes q),
HasProcesses e q
) =>
CallbacksEff tag eLoop q ->
Eff e (Endpoint tag)
start = E.start
-- | Execute the server loop, that dispatches incoming events
-- to either a set of 'Callbacks' or 'CallbacksEff'.
--
-- @since 0.29.1
startLink ::
forall (tag :: Type) eLoop q e.
( TangibleCallbacks tag eLoop q,
E.Server (Server tag eLoop q) (Processes q),
FilteredLogging (Processes q),
HasProcesses e q
) =>
CallbacksEff tag eLoop q ->
Eff e (Endpoint tag)
startLink = E.startLink
-- | Phantom type to indicate a callback based 'E.Server' instance.
--
-- @since 0.27.0
data Server tag eLoop e deriving (Typeable)
instance ToTypeLogMsg tag => ToTypeLogMsg (Server tag eLoop e) where
toTypeLogMsg _ = toTypeLogMsg (Proxy @tag)
-- | The constraints for a /tangible/ 'Server' instance.
--
-- @since 0.27.0
type TangibleCallbacks tag eLoop e =
( HasProcesses eLoop e,
ToTypeLogMsg tag,
Typeable e,
Typeable eLoop,
Typeable tag
)
-- | The name/id of a 'Server' for logging purposes.
--
-- @since 0.24.0
newtype ServerId (tag :: Type) = MkServerId {_fromServerId :: T.Text}
deriving (Typeable, NFData, Ord, Eq, IsString)
instance ToTypeLogMsg tag => ToTypeLogMsg (ServerId tag) where
toTypeLogMsg _ = toTypeLogMsg (Proxy @tag) <> packLogMsg "_server_id"
instance ToLogMsg (ServerId tag) where
toLogMsg x = coerce x
instance (ToTypeLogMsg tag) => Show (ServerId tag) where
showsPrec d px@(MkServerId x) =
showParen
(d >= 10)
( showString (T.unpack x)
. showString "_"
. shows (toTypeLogMsg px)
)
instance (ToLogMsg (E.Init (Server tag eLoop e)), TangibleCallbacks tag eLoop e) => E.Server (Server (tag :: Type) eLoop e) (Processes e) where
type ServerPdu (Server tag eLoop e) = tag
type ServerEffects (Server tag eLoop e) (Processes e) = eLoop
data Init (Server tag eLoop e) = MkServer
{ genServerId :: ServerId tag,
genServerRunEffects :: forall x. (Endpoint tag -> Eff eLoop x -> Eff (Processes e) x),
genServerOnEvent :: Endpoint tag -> Event tag -> Eff eLoop ()
}
deriving (Typeable)
runEffects myEp svr = genServerRunEffects svr myEp
onEvent myEp svr = genServerOnEvent svr myEp
instance forall (tag :: Type) (e1 :: [Type -> Type]) (e2 :: [Type -> Type]). ToLogMsg (E.Init (Server tag e1 e2)) where
toLogMsg x = toLogMsg (genServerId x)
instance (TangibleCallbacks tag eLoop e) => NFData (E.Init (Server (tag :: Type) eLoop e)) where
rnf (MkServer x y z) = rnf x `seq` y `seq` z `seq` ()
instance forall tag eLoop e. (TangibleCallbacks tag eLoop e) => Show (E.Init (Server (tag :: Type) eLoop e)) where
showsPrec d svr =
showParen
(d >= 10)
( showsPrec 11 (genServerId svr)
. showChar ' '
. shows (toTypeLogMsg (Proxy @tag))
. showString " callback-server"
)
-- ** Smart Constructors for 'Callbacks'
-- | A convenience type alias for callbacks that do not
-- need a custom effect.
--
-- @since 0.29.1
type Callbacks tag e = CallbacksEff tag (Processes e) e
-- | A smart constructor for 'Callbacks'.
--
-- @since 0.29.1
callbacks ::
forall tag q.
(Endpoint tag -> Event tag -> Eff (Processes q) ()) ->
ServerId tag ->
Callbacks tag q
callbacks evtCb i = callbacksEff (const id) evtCb i
-- | A simple smart constructor for 'Callbacks'.
--
-- @since 0.29.1
onEvent ::
forall tag q.
(Event tag -> Eff (Processes q) ()) ->
ServerId (tag :: Type) ->
Callbacks tag q
onEvent = onEventEff id
-- ** Smart Constructors for 'CallbacksEff'
-- | A convenience type alias for __effectful__ callback based 'E.Server' instances.
--
-- See 'Callbacks'.
--
-- @since 0.29.1
type CallbacksEff tag eLoop e = E.Init (Server tag eLoop e)
-- | A smart constructor for 'CallbacksEff'.
--
-- @since 0.29.1
callbacksEff ::
forall tag eLoop q.
(forall x. Endpoint tag -> Eff eLoop x -> Eff (Processes q) x) ->
(Endpoint tag -> Event tag -> Eff eLoop ()) ->
ServerId tag ->
CallbacksEff tag eLoop q
callbacksEff a b c = MkServer c a b
-- | A simple smart constructor for 'CallbacksEff'.
--
-- @since 0.29.1
onEventEff ::
(forall a. Eff eLoop a -> Eff (Processes q) a) ->
(Event tag -> Eff eLoop ()) ->
ServerId (tag :: Type) ->
CallbacksEff tag eLoop q
onEventEff h f i = callbacksEff (const h) (const f) i