-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Message passing concurrency as extensible-effect -- -- Please see the README on GitHub at -- https://github.com/sheyll/extensible-effects-concurrent#readme @package extensible-effects-concurrent @version 0.23.0 -- | Add-ons to Exception and Exception module Control.Eff.ExceptionExtra -- | Catch Exception thrown by an effect. liftTry :: forall e r a. (HasCallStack, Exception e, Lifted IO r) => Eff r a -> Eff r (Either e a) -- | Very similar to liftEither but for Maybes. Unlike -- liftMaybe this will throw the given value (instead of using -- Fail). maybeThrow :: Member (Exc x) e => x -> Maybe a -> Eff e a instance Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Reader.Lazy.Reader x : e)) instance Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Reader.Lazy.Reader x : e)) instance Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Reader.Lazy.Reader x : e)) instance Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Reader.Strict.Reader x : e)) instance Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Reader.Strict.Reader x : e)) instance Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Reader.Strict.Reader x : e)) instance Control.Monad.Catch.MonadThrow m => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff '[Control.Eff.Internal.Lift m]) instance Control.Monad.Catch.MonadCatch m => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff '[Control.Eff.Internal.Lift m]) instance Control.Monad.Catch.MonadMask m => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff '[Control.Eff.Internal.Lift m]) instance Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Exception.Exc x : e)) instance Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Exception.Exc x : e)) instance Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Exception.Exc x : e)) -- | An RFC 5434 inspired log message and convenience functions for logging -- them. module Control.Eff.Log.Message -- | A message data type inspired by the RFC-5424 Syslog Protocol data LogMessage MkLogMessage :: !Facility -> !Severity -> Maybe UTCTime -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> [StructuredDataElement] -> Maybe ThreadId -> Maybe SrcLoc -> Text -> LogMessage [_lmFacility] :: LogMessage -> !Facility [_lmSeverity] :: LogMessage -> !Severity [_lmTimestamp] :: LogMessage -> Maybe UTCTime [_lmHostname] :: LogMessage -> Maybe Text [_lmAppName] :: LogMessage -> Maybe Text [_lmProcessId] :: LogMessage -> Maybe Text [_lmMessageId] :: LogMessage -> Maybe Text [_lmStructuredData] :: LogMessage -> [StructuredDataElement] [_lmThreadId] :: LogMessage -> Maybe ThreadId [_lmSrcLoc] :: LogMessage -> Maybe SrcLoc [_lmMessage] :: LogMessage -> Text -- | A lens for the Facility of a LogMessage lmFacility :: Functor f => (Facility -> f Facility) -> LogMessage -> f LogMessage -- | A lens for the Severity of a LogMessage lmSeverity :: Functor f => (Severity -> f Severity) -> LogMessage -> f LogMessage -- | A lens for the UTC time of a LogMessage The function -- setLogMessageTimestamp can be used to set the field. lmTimestamp :: Functor f => (Maybe UTCTime -> f (Maybe UTCTime)) -> LogMessage -> f LogMessage -- | A lens for the hostname of a LogMessage The function -- setLogMessageHostname can be used to set the field. lmHostname :: Functor f => (Maybe Text -> f (Maybe Text)) -> LogMessage -> f LogMessage -- | A lens for the RFC 5424 application name of a LogMessage -- -- One useful pattern for using this field, is to implement log filters -- that allow info and debug message from the application itself while -- only allowing warning and error messages from third party libraries: -- --
--   debugLogsForAppName myAppName lm =
--     view lmAppName lm == Just myAppName || lmSeverityIsAtLeast warningSeverity lm
--   
-- -- This concept is also implemented in discriminateByAppName. lmAppName :: Functor f => (Maybe Text -> f (Maybe Text)) -> LogMessage -> f LogMessage -- | A lens for a user defined of process id of a LogMessage lmProcessId :: Functor f => (Maybe Text -> f (Maybe Text)) -> LogMessage -> f LogMessage -- | A lens for a user defined message id of a LogMessage lmMessageId :: Functor f => (Maybe Text -> f (Maybe Text)) -> LogMessage -> f LogMessage -- | A lens for the StructuredDataElement of a LogMessage lmStructuredData :: Functor f => ([StructuredDataElement] -> f [StructuredDataElement]) -> LogMessage -> f LogMessage -- | A lens for the SrcLoc of a LogMessage lmSrcLoc :: Functor f => (Maybe SrcLoc -> f (Maybe SrcLoc)) -> LogMessage -> f LogMessage -- | A lens for the ThreadId of a LogMessage The function -- setLogMessageThreadId can be used to set the field. lmThreadId :: Functor f => (Maybe ThreadId -> f (Maybe ThreadId)) -> LogMessage -> f LogMessage -- | A lens for the user defined textual message of a LogMessage lmMessage :: Functor f => (Text -> f Text) -> LogMessage -> f LogMessage -- | Put the source location of the given callstack in lmSrcLoc setCallStack :: CallStack -> LogMessage -> LogMessage -- | Prefix the lmMessage. prefixLogMessagesWith :: Text -> LogMessage -> LogMessage -- | An IO action that sets the current UTC time in lmTimestamp. setLogMessageTimestamp :: LogMessage -> IO LogMessage -- | An IO action appends the the ThreadId of the calling process -- (see myThreadId) to lmMessage. setLogMessageThreadId :: LogMessage -> IO LogMessage -- | An IO action that sets the current hosts fully qualified hostname in -- lmHostname. setLogMessageHostname :: LogMessage -> IO LogMessage -- | Construct a LogMessage with errorSeverity errorMessage :: HasCallStack => Text -> LogMessage -- | Construct a LogMessage with informationalSeverity infoMessage :: HasCallStack => Text -> LogMessage -- | Construct a LogMessage with debugSeverity debugMessage :: HasCallStack => Text -> LogMessage -- | Things that can become a LogMessage class ToLogMessage a -- | Convert the value to a LogMessage toLogMessage :: ToLogMessage a => a -> LogMessage -- | Construct a LogMessage with errorSeverity errorMessageIO :: (HasCallStack, MonadIO m) => Text -> m LogMessage -- | Construct a LogMessage with informationalSeverity infoMessageIO :: (HasCallStack, MonadIO m) => Text -> m LogMessage -- | Construct a LogMessage with debugSeverity debugMessageIO :: (HasCallStack, MonadIO m) => Text -> m LogMessage -- | The filter predicate for message that shall be logged. -- -- See Control.Eff.Log#LogPredicate type LogPredicate = LogMessage -> Bool -- | All messages. -- -- See Control.Eff.Log.Message#PredefinedPredicates for more -- predicates. allLogMessages :: LogPredicate -- | No messages. -- -- See Control.Eff.Log.Message#PredefinedPredicates for more -- predicates. noLogMessages :: LogPredicate -- | Match LogMessages that have exactly the given severity. See -- lmSeverityIsAtLeast. -- -- See Control.Eff.Log.Message#PredefinedPredicates for more -- predicates. lmSeverityIs :: Severity -> LogPredicate -- | Match LogMessages that have the given severity or worse. -- See lmSeverityIs. -- -- See Control.Eff.Log.Message#PredefinedPredicates for more -- predicates. lmSeverityIsAtLeast :: Severity -> LogPredicate -- | Match LogMessages whose lmMessage starts with the given -- string. -- -- See Control.Eff.Log.Message#PredefinedPredicates for more -- predicates. lmMessageStartsWith :: Text -> LogPredicate -- | Apply a LogPredicate based on the lmAppName and delegate -- to one of two LogPredicates. -- -- One useful application for this is to allow info and debug message -- from one application, e.g. the current application itself, while at -- the same time allowing only warning and error messages from third -- party libraries. -- -- See Control.Eff.Log.Message#PredefinedPredicates for more -- predicates. discriminateByAppName :: Text -> LogPredicate -> LogPredicate -> LogPredicate -- | RFC-5424 defines how structured data can be included in a log message. data StructuredDataElement SdElement :: !Text -> ![SdParameter] -> StructuredDataElement [_sdElementId] :: StructuredDataElement -> !Text [_sdElementParameters] :: StructuredDataElement -> ![SdParameter] -- | Component of an RFC-5424 StructuredDataElement data SdParameter MkSdParameter :: !Text -> !Text -> SdParameter -- | A lens for the key or ID of a group of RFC 5424 key-value pairs. sdElementId :: Functor f => (Text -> f Text) -> StructuredDataElement -> f StructuredDataElement -- | A lens for SdParameters sdElementParameters :: Functor f => ([SdParameter] -> f [SdParameter]) -> StructuredDataElement -> f StructuredDataElement -- | An rfc 5424 severity data Severity -- | Smart constructor for the RFC-5424 emergency LogMessage -- Severity. This corresponds to the severity value 0. See -- lmSeverity. emergencySeverity :: Severity -- | Smart constructor for the RFC-5424 alert LogMessage -- Severity. This corresponds to the severity value 1. See -- lmSeverity. alertSeverity :: Severity -- | Smart constructor for the RFC-5424 critical LogMessage -- Severity. This corresponds to the severity value 2. See -- lmSeverity. criticalSeverity :: Severity -- | Smart constructor for the RFC-5424 error LogMessage -- Severity. This corresponds to the severity value 3. See -- lmSeverity. errorSeverity :: Severity -- | Smart constructor for the RFC-5424 warning LogMessage -- Severity. This corresponds to the severity value 4. See -- lmSeverity. warningSeverity :: Severity -- | Smart constructor for the RFC-5424 notice LogMessage -- Severity. This corresponds to the severity value 5. See -- lmSeverity. noticeSeverity :: Severity -- | Smart constructor for the RFC-5424 informational -- LogMessage Severity. This corresponds to the severity -- value 6. See lmSeverity. informationalSeverity :: Severity -- | Smart constructor for the RFC-5424 debug LogMessage -- Severity. This corresponds to the severity value 7. See -- lmSeverity. debugSeverity :: Severity -- | An rfc 5424 facility newtype Facility Facility :: Int -> Facility [fromFacility] :: Facility -> Int -- | Smart constructor for the RFC-5424 LogMessage facility -- kernelMessages. See lmFacility. kernelMessages :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- userLevelMessages. See lmFacility. userLevelMessages :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- mailSystem. See lmFacility. mailSystem :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- systemDaemons. See lmFacility. systemDaemons :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- securityAuthorizationMessages4. See lmFacility. securityAuthorizationMessages4 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- linePrinterSubsystem. See lmFacility. linePrinterSubsystem :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- networkNewsSubsystem. See lmFacility. networkNewsSubsystem :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- uucpSubsystem. See lmFacility. uucpSubsystem :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- clockDaemon. See lmFacility. clockDaemon :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- securityAuthorizationMessages10. See lmFacility. securityAuthorizationMessages10 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- ftpDaemon. See lmFacility. ftpDaemon :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- ntpSubsystem. See lmFacility. ntpSubsystem :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- logAuditFacility. See lmFacility. logAuditFacility :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- logAlertFacility. See lmFacility. logAlertFacility :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- clockDaemon2. See lmFacility. clockDaemon2 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local0. See lmFacility. local0 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local1. See lmFacility. local1 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local2. See lmFacility. local2 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local3. See lmFacility. local3 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local4. See lmFacility. local4 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local5. See lmFacility. local5 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local6. See lmFacility. local6 :: Facility -- | Smart constructor for the RFC-5424 LogMessage facility -- local7. See lmFacility. local7 :: Facility instance Control.Eff.Log.Message.ToLogMessage Control.Eff.Log.Message.LogMessage instance Control.Eff.Log.Message.ToLogMessage Data.Text.Internal.Text instance Data.String.IsString Control.Eff.Log.Message.LogMessage instance GHC.Generics.Generic Control.Eff.Log.Message.LogMessage instance GHC.Classes.Eq Control.Eff.Log.Message.LogMessage instance Control.DeepSeq.NFData Control.Eff.Log.Message.Facility instance GHC.Generics.Generic Control.Eff.Log.Message.Facility instance GHC.Show.Show Control.Eff.Log.Message.Facility instance GHC.Classes.Ord Control.Eff.Log.Message.Facility instance GHC.Classes.Eq Control.Eff.Log.Message.Facility instance Control.DeepSeq.NFData Control.Eff.Log.Message.Severity instance GHC.Generics.Generic Control.Eff.Log.Message.Severity instance GHC.Classes.Ord Control.Eff.Log.Message.Severity instance GHC.Classes.Eq Control.Eff.Log.Message.Severity instance GHC.Show.Show Control.Eff.Log.Message.StructuredDataElement instance GHC.Generics.Generic Control.Eff.Log.Message.StructuredDataElement instance GHC.Classes.Ord Control.Eff.Log.Message.StructuredDataElement instance GHC.Classes.Eq Control.Eff.Log.Message.StructuredDataElement instance GHC.Show.Show Control.Eff.Log.Message.SdParameter instance GHC.Generics.Generic Control.Eff.Log.Message.SdParameter instance GHC.Classes.Ord Control.Eff.Log.Message.SdParameter instance GHC.Classes.Eq Control.Eff.Log.Message.SdParameter instance Data.Default.Class.Default Control.Eff.Log.Message.LogMessage instance GHC.Show.Show Control.Eff.Log.Message.LogMessage instance Control.DeepSeq.NFData Control.Eff.Log.Message.LogMessage instance Data.Default.Class.Default Control.Eff.Log.Message.Facility instance GHC.Show.Show Control.Eff.Log.Message.Severity instance Data.Default.Class.Default Control.Eff.Log.Message.Severity instance Control.DeepSeq.NFData Control.Eff.Log.Message.StructuredDataElement instance Control.DeepSeq.NFData Control.Eff.Log.Message.SdParameter -- | Rendering functions for LogMessages. module Control.Eff.Log.MessageRenderer -- | LogMessage rendering function type LogMessageRenderer a = LogMessage -> a -- | Render the LogMessage to contain the severity, message, -- message-id, pid. -- -- Omit hostname, PID and timestamp. -- -- Render the header using renderSyslogSeverity -- -- Useful for logging to devlog renderLogMessageSyslog :: LogMessageRenderer Text -- | Render a LogMessage human readable, for console logging renderLogMessageConsoleLog :: LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-3164. renderRFC3164 :: LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-3164 but -- use RFC5424 time stamps. renderRFC3164WithRFC5424Timestamps :: LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-3164 but -- use the custom LogMessageTimeRenderer. renderRFC3164WithTimestamp :: LogMessageTimeRenderer -> LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-5424. -- -- Equivalent to renderRFC5424Header <> const " " -- <> renderLogMessageBody. renderRFC5424 :: LogMessageRenderer Text -- | Render the header and strucuted data of a LogMessage according -- to the rules in the RFC-5424, but do not render the lmMessage. renderRFC5424Header :: LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-5424, -- like renderRFC5424 but suppress the source location -- information. -- -- Equivalent to renderRFC5424Header <> const " " -- <> renderLogMessageBodyNoLocation. renderRFC5424NoLocation :: LogMessageRenderer Text -- | Render the severity and facility as described in RFC-3164 -- -- Render e.g. as <192>. -- -- Useful as header for syslog compatible log output. renderSyslogSeverityAndFacility :: LogMessageRenderer Text -- | Render the source location as: at filepath:linenumber. renderLogMessageSrcLoc :: LogMessageRenderer (Maybe Text) -- | Render a field of a LogMessage using the corresponsing lens. renderMaybeLogMessageLens :: Text -> Getter LogMessage (Maybe Text) -> LogMessageRenderer Text -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBodyNoLocation :: LogMessageRenderer Text -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBody :: LogMessageRenderer Text -- | Print the body of a LogMessage with fix size fields (60) -- for the message itself and 30 characters for the location renderLogMessageBodyFixWidth :: LogMessageRenderer Text -- | A rendering function for the lmTimestamp field. data LogMessageTimeRenderer -- | Make a LogMessageTimeRenderer using formatTime in the -- defaultLocale. mkLogMessageTimeRenderer :: String -> LogMessageTimeRenderer -- | Don't render the time stamp suppressTimestamp :: LogMessageTimeRenderer -- | Render the time stamp using "%h %d %H:%M:%S" rfc3164Timestamp :: LogMessageTimeRenderer -- | Render the time stamp to iso8601DateFormat (Just -- "%H:%M:%S%6QZ") rfc5424Timestamp :: LogMessageTimeRenderer -- | Render the time stamp like rfc5424Timestamp does, but omit the -- terminal Z character. rfc5424NoZTimestamp :: LogMessageTimeRenderer -- | The LogWriter type encapsulates an effectful function to write -- LogMessages. -- -- Used in conjunction with the HandleLogWriter class, it can be -- used to write messages from within an effectful computation. module Control.Eff.Log.Writer -- | A function that takes a log message and returns an effect that -- logs the message. newtype LogWriter writerM MkLogWriter :: (LogMessage -> writerM ()) -> LogWriter writerM [runLogWriter] :: LogWriter writerM -> LogMessage -> writerM () -- | A Reader specialized for LogWriters -- -- The existing Reader couldn't be used together with -- SetMember, so this lazy reader was written, specialized to -- reading LogWriter. data LogWriterReader h v -- | Modify the current LogWriter. localLogWriterReader :: forall h e a. SetMember LogWriterReader (LogWriterReader h) e => (LogWriter h -> LogWriter h) -> Eff e a -> Eff e a -- | Get the current LogWriter. askLogWriter :: SetMember LogWriterReader (LogWriterReader h) e => Eff e (LogWriter h) -- | Provide the LogWriter -- -- Exposed for custom extensions, if in doubt use withLogging. runLogWriterReader :: LogWriter h -> Eff (LogWriterReader h : e) a -> Eff e a -- | The instances of this class are the monads that define (side-) -- effect(s) of writting logs. class HandleLogWriter (writerEff :: Type -> Type) where { -- | A list of effects that are required for writing the log messages. For -- example 'Lift IO' or '[]' for pure log writers. type family LogWriterEffects writerEff :: [Type -> Type]; } -- | Run the side effect of a LogWriter in a compatible Eff. handleLogWriterEffect :: (HandleLogWriter writerEff, LogWriterEffects writerEff <:: e) => writerEff () -> Eff e () -- | Write a message using the LogWriter found in the environment. -- -- The semantics of this function are a combination of -- runLogWriter and handleLogWriterEffect, with the -- LogWriter read from a LogWriterReader. liftWriteLogMessage :: (HandleLogWriter writerEff, SetMember LogWriterReader (LogWriterReader writerEff) e, LogWriterEffects writerEff <:: e) => LogMessage -> Eff e () -- | This LogWriter will discard all messages. -- -- NOTE: This is just an alias for def noOpLogWriter :: Applicative m => LogWriter m -- | A phantom type for the HandleLogWriter class for pure -- LogWriters newtype PureLogWriter a MkPureLogWriter :: Identity a -> PureLogWriter a [runPureLogWriter] :: PureLogWriter a -> Identity a -- | A LogWriter that applies a predicate to the LogMessage -- and delegates to to the given writer of the predicate is satisfied. filteringLogWriter :: Monad e => LogPredicate -> LogWriter e -> LogWriter e -- | A LogWriter that applies a function to the LogMessage -- and delegates the result to to the given writer. mappingLogWriter :: (LogMessage -> LogMessage) -> LogWriter e -> LogWriter e -- | Like mappingLogWriter allow the function that changes the -- LogMessage to have effects. mappingLogWriterM :: Monad e => (LogMessage -> e LogMessage) -> LogWriter e -> LogWriter e instance GHC.Base.Monad Control.Eff.Log.Writer.PureLogWriter instance GHC.Base.Functor Control.Eff.Log.Writer.PureLogWriter instance GHC.Base.Applicative Control.Eff.Log.Writer.PureLogWriter instance Control.Eff.Log.Writer.HandleLogWriter Control.Eff.Log.Writer.PureLogWriter instance Control.Eff.Log.Writer.HandleLogWriter GHC.Types.IO instance Control.Eff.Internal.Handle (Control.Eff.Log.Writer.LogWriterReader h) e a (Control.Eff.Log.Writer.LogWriter h -> k) instance (Control.Monad.Base.MonadBase m m, Control.Eff.Internal.LiftedBase m r) => Control.Monad.Trans.Control.MonadBaseControl m (Control.Eff.Internal.Eff (Control.Eff.Log.Writer.LogWriterReader h : r)) instance (Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e)) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Log.Writer.LogWriterReader h : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e)) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Log.Writer.LogWriterReader h : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e)) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Log.Writer.LogWriterReader h : e)) instance GHC.Base.Applicative w => Data.Default.Class.Default (Control.Eff.Log.Writer.LogWriter w) -- | A memory efficient, streaming, logging effect with support for -- efficiently not logging when no logs are required. -- -- Good support for logging to a file or to the network, as well as -- asynchronous logging in another thread. module Control.Eff.Log.Handler -- | Log a message. -- -- All logging goes through this function. -- -- This function is the only place where the LogPredicate is -- applied. -- -- Also, LogMessages are evaluated using deepseq, -- after they pass the LogPredicate. logMsg :: forall e. (HasCallStack, Member Logs e) => LogMessage -> Eff e () -- | Log a Text as LogMessage with a given Severity. logWithSeverity :: forall e. (HasCallStack, Member Logs e) => Severity -> Text -> Eff e () -- | Log a Text as LogMessage with a given Severity. logWithSeverity' :: forall e. (HasCallStack, Member Logs e) => Severity -> String -> Eff e () -- | Log a String as emergencySeverity. logEmergency :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a String as emergencySeverity. logEmergency' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a message with alertSeverity. logAlert :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a message with alertSeverity. logAlert' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a criticalSeverity message. logCritical :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a criticalSeverity message. logCritical' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a errorSeverity message. logError :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a errorSeverity message. logError' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a warningSeverity message. logWarning :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a warningSeverity message. logWarning' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a noticeSeverity message. logNotice :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a noticeSeverity message. logNotice' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a informationalSeverity message. logInfo :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a informationalSeverity message. logInfo' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a debugSeverity message. logDebug :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a debugSeverity message. logDebug' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Include LogMessages that match a LogPredicate. -- -- excludeLogMessages p allows log message to be logged if p -- m -- -- Although it is enough if the previous predicate holds. See -- excludeLogMessages and modifyLogPredicate. -- -- See Control.Eff.Log#LogPredicate includeLogMessages :: forall e a. Member Logs e => LogPredicate -> Eff e a -> Eff e a -- | Exclude LogMessages that match a LogPredicate. -- -- excludeLogMessages p discards logs if p m -- -- Also the previous predicate must also hold for a message to be logged. -- See excludeLogMessages and modifyLogPredicate. -- -- See Control.Eff.Log#LogPredicate excludeLogMessages :: forall e a. Member Logs e => LogPredicate -> Eff e a -> Eff e a -- | Keep only those messages, for which a predicate holds. -- -- E.g. to keep only messages which begin with OMG: -- --
--   exampleLogPredicate :: IO Int
--   exampleLogPredicate =
--       runLift
--     $ withLogging consoleLogWriter
--     $ do logMsg "test"
--          setLogPredicate (\ msg -> case view lmMessage msg of
--                                     'O':'M':'G':_ -> True
--                                     _             -> False)
--                            (do logMsg "this message will not be logged"
--                                logMsg "OMG logged"
--                                return 42)
--   
-- -- In order to also delegate to the previous predicate, use -- modifyLogPredicate -- -- See Control.Eff.Log#LogPredicate setLogPredicate :: forall r b. (Member Logs r, HasCallStack) => LogPredicate -> Eff r b -> Eff r b -- | Change the LogPredicate. -- -- Other than setLogPredicate this function allows to include the -- previous predicate, too. -- -- For to discard all messages currently no satisfying the predicate and -- also all messages that are to long: -- --
--   modifyLogPredicate (previousPredicate msg -> previousPredicate msg && length (lmMessage msg) < 29 )
--                      (do logMsg "this message will not be logged"
--                          logMsg "this message might be logged")
--   
-- -- See Control.Eff.Log#LogPredicate modifyLogPredicate :: forall e b. (Member Logs e, HasCallStack) => (LogPredicate -> LogPredicate) -> Eff e b -> Eff e b -- | Get the current Logs filter/transformer function. -- -- See Control.Eff.Log#LogPredicate askLogPredicate :: forall e. Member Logs e => Eff e LogPredicate -- | Replace the current LogWriter. To add an additional log message -- consumer use addLogWriter setLogWriter :: forall h e a. LogsTo h e => LogWriter h -> Eff e a -> Eff e a -- | Combine the effects of a given LogWriter and the existing one. -- --
--   import Data.Text    as T
--   import Data.Text.IO as T
--   
--   exampleAddLogWriter :: IO ()
--   exampleAddLogWriter = go >>= T.putStrLn
--    where go = fmap (unlines . map renderLogMessageConsoleLog . snd)
--                 $  runLift
--                 $  runCaptureLogWriter
--                 $  withLogging captureLogWriter
--                 $  addLogWriter (mappingLogWriter (lmMessage %~ ("CAPTURED "++)) captureLogWriter)
--                 $  addLogWriter (filteringLogWriter severeMessages (mappingLogWriter (lmMessage %~ ("TRACED "++)) debugTraceLogWriter))
--                 $  do
--                       logEmergency "test emergencySeverity 1"
--                       logCritical "test criticalSeverity 2"
--                       logAlert "test alertSeverity 3"
--                       logError "test errorSeverity 4"
--                       logWarning "test warningSeverity 5"
--                       logInfo "test informationalSeverity 6"
--                       logDebug "test debugSeverity 7"
--          severeMessages = view (lmSeverity . to (<= errorSeverity))
--   
addLogWriter :: forall h e a. (HasCallStack, LogsTo h e, Monad h) => LogWriter h -> Eff e a -> Eff e a -- | Change the current LogWriter. modifyLogWriter :: forall h e a. LogsTo h e => (LogWriter h -> LogWriter h) -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriter censorLogs :: LogsTo h e => (LogMessage -> LogMessage) -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression, -- as in censorLogs but with a effectful function. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriterM censorLogsM :: (LogsTo h e, Monad h) => (LogMessage -> h LogMessage) -> Eff e a -> Eff e a -- | This effect sends LogMessages and is a reader for a -- LogPredicate. -- -- Logs are sent via logMsg; for more information about log -- predicates, see Control.Eff.Log#LogPredicate -- -- This effect is handled via withLogging. data Logs v -- | A constraint alias for effects that requires a LogWriterReader, -- as well as that the contained LogWriterReader has a -- HandleLogWriter instance. -- -- The requirements of this constraint are provided by: -- -- type LogsTo h e = (Member Logs e, HandleLogWriter h, LogWriterEffects h <:: e, SetMember LogWriterReader (LogWriterReader h) e) -- | Handle the Logs and LogWriterReader effects. -- -- It installs the given LogWriter, which determines the -- underlying LogWriter type parameter. -- -- Example: -- --
--   exampleWithLogging :: IO ()
--   exampleWithLogging =
--       runLift
--     $ withLogging consoleLogWriter
--     $ logDebug "Oh, hi there"
--   
withLogging :: forall h e a. (Applicative h, LogsTo h (Logs : (LogWriterReader h : e))) => LogWriter h -> Eff (Logs : (LogWriterReader h : e)) a -> Eff e a -- | Handles the Logs and LogWriterReader effects. -- -- By default it uses the noOpLogWriter, but using -- setLogWriter the LogWriter can be replaced. -- -- This is like withLogging applied to noOpLogWriter -- -- Example: -- --
--   exampleWithSomeLogging :: ()
--   exampleWithSomeLogging =
--       run
--     $ withSomeLogging @PureLogWriter
--     $ logDebug "Oh, hi there"
--   
withSomeLogging :: forall h e a. (Applicative h, LogsTo h (Logs : (LogWriterReader h : e))) => Eff (Logs : (LogWriterReader h : e)) a -> Eff e a -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withLogging. runLogs :: forall h e b. LogsTo h (Logs : e) => LogPredicate -> Eff (Logs : e) b -> Eff e b -- | Consume log messages. -- -- Exposed for custom extensions, if in doubt use withLogging. -- -- Respond to all LogMessages logged from the given action, up to -- any MonadBaseControl liftings. -- -- Note that all logging is done through logMsg and that means -- only messages passing the LogPredicate are received. -- -- The LogMessages are consumed once they are passed to the -- given callback function, previous respondToLogMessage -- invocations further up in the call stack will not get the messages -- anymore. -- -- Use interceptLogMessages if the messages shall be passed any -- previous handler. -- -- NOTE: The effects of this function are lost when using -- MonadBaseControl, MonadMask, MonadCatch and -- MonadThrow. -- -- In contrast the functions based on modifying the LogWriter, -- such as addLogWriter or censorLogs, are save to use in -- combination with the aforementioned liftings. respondToLogMessage :: forall r b. Member Logs r => (LogMessage -> Eff r ()) -> Eff r b -> Eff r b -- | Change the LogMessages using an effectful function. -- -- Exposed for custom extensions, if in doubt use withLogging. -- -- This differs from respondToLogMessage in that the intercepted -- messages will be written either way, albeit in altered form. -- -- NOTE: The effects of this function are lost when using -- MonadBaseControl, MonadMask, MonadCatch and -- MonadThrow. -- -- In contrast the functions based on modifying the LogWriter, -- such as addLogWriter or censorLogs, are save to use in -- combination with the aforementioned liftings. interceptLogMessages :: forall r b. Member Logs r => (LogMessage -> Eff r LogMessage) -> Eff r b -> Eff r b instance (Control.Monad.Base.MonadBase m m, Control.Eff.Internal.LiftedBase m e, Control.Eff.Log.Handler.LogsTo m (Control.Eff.Log.Handler.Logs : e)) => Control.Monad.Trans.Control.MonadBaseControl m (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e), Control.Eff.Log.Handler.LogsTo m (Control.Eff.Log.Handler.Logs : e)) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e), Control.Eff.Log.Handler.LogsTo m (Control.Eff.Log.Handler.Logs : e)) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) instance Control.Eff.Internal.Handle Control.Eff.Log.Handler.Logs e a (Control.Eff.Log.Message.LogPredicate -> k) instance (Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e)) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) -- | The message passing effect. -- -- This module describes an abstract message passing effect, and a -- process effect, mimicking Erlang's process and message semantics. -- -- Two scheduler implementations for the Process effect are -- provided: -- -- module Control.Eff.Concurrent.Process -- | The process effect is the basis for message passing concurrency. This -- effect describes an interface for concurrent, communicating isolated -- processes identified uniquely by a process-id. -- -- Processes can raise exceptions that can be caught, exit gracefully or -- with an error, or be killed by other processes, with the option of -- ignoring the shutdown request. -- -- Process Scheduling is implemented in different modules. All scheduler -- implementations should follow some basic rules: -- -- data Process (r :: [Type -> Type]) b -- | Remove all messages from the process' message queue [FlushMessages] :: Process r (ResumeProcess [StrictDynamic]) -- | In cooperative schedulers, this will give processing time to the -- scheduler. Every other operation implicitly serves the same purpose. [YieldProcess] :: Process r (ResumeProcess ()) -- | Return the current ProcessId [SelfPid] :: Process r (ResumeProcess ProcessId) -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. [Spawn] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Get the process state (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe ProcessState)) -- | Shutdown the process; irregardless of the exit reason, this function -- never returns, [Shutdown] :: Interrupt 'NoRecovery -> Process r a -- | Shutdown another process immediately, the other process has no way of -- handling this! [SendShutdown] :: ProcessId -> Interrupt 'NoRecovery -> Process r (ResumeProcess ()) -- | Request that another a process interrupts. The targeted process is -- interrupted and gets an Interrupted, the target process may -- decide to ignore the interrupt and continue as if nothing happened. [SendInterrupt] :: ProcessId -> Interrupt 'Recoverable -> Process r (ResumeProcess ()) -- | Send a message to a process addressed by the ProcessId. Sending -- a message should always succeed and return immediately, -- even if the destination process does not exist, or does not accept -- messages of the given type. [SendMessage] :: ProcessId -> StrictDynamic -> Process r (ResumeProcess ()) -- | Receive a message that matches a criteria. This should block until an -- a message was received. The message is returned as a -- ResumeProcess value. The function should also return if an -- exception was caught or a shutdown was requested. [ReceiveSelectedMessage] :: forall r a. MessageSelector a -> Process r (ResumeProcess a) -- | Generate a unique Int for the current process. [MakeReference] :: Process r (ResumeProcess Int) -- | Monitor another process. When the monitored process exits a -- ProcessDown is sent to the calling process. The return value is -- a unique identifier for that monitor. There can be multiple monitors -- on the same process, and a message for each will be sent. If the -- process is already dead, the ProcessDown message will be sent -- immediately, without exit reason [Monitor] :: ProcessId -> Process r (ResumeProcess MonitorReference) -- | Remove a monitor. [Demonitor] :: MonitorReference -> Process r (ResumeProcess ()) -- | Connect the calling process to another process, such that if one of -- the processes crashes (i.e. isCrash returns True), the -- other is shutdown with the Interrupt -- LinkedProcessCrashed. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Data flows between Processes via these messages. -- -- This is just a newtype wrapper around Dynamic. The reason this -- type exists is to force construction through the code in this module, -- which always evaluates a message to normal form before -- sending it to another process. data StrictDynamic -- | Deeply evaluate the given value and wrap it into a -- StrictDynamic. toStrictDynamic :: (Typeable a, NFData a) => a -> StrictDynamic -- | Convert a StrictDynamic back to a value. fromStrictDynamic :: Typeable a => StrictDynamic -> Maybe a -- | Convert a StrictDynamic back to an unwrapped Dynamic. unwrapStrictDynamic :: StrictDynamic -> Dynamic -- | Each process is identified by a single process id, that stays constant -- throughout the life cycle of a process. Also, message sending relies -- on these values to address messages to processes. newtype ProcessId ProcessId :: Int -> ProcessId [_fromProcessId] :: ProcessId -> Int fromProcessId :: Iso' ProcessId Int -- | Cons Process onto a list of effects. type ConsProcess r = Process r : r -- | Every Process action returns it's actual result wrapped in this -- type. It will allow to signal errors as well as pass on normal results -- such as incoming messages. data ResumeProcess v -- | The current operation of the process was interrupted with a -- Interrupt. If isRecoverable holds for the given reason, -- the process may choose to continue. [Interrupted] :: Interrupt 'Recoverable -> ResumeProcess v -- | The process may resume to do work, using the given result. [ResumeWith] :: a -> ResumeProcess a -- | The state that a Process is currently in. data ProcessState -- | The process has just been started but not scheduled yet. ProcessBooting :: ProcessState -- | The process yielded it's time slice ProcessIdle :: ProcessState -- | The process is busy with non-blocking ProcessBusy :: ProcessState -- | The process is busy with sending a message ProcessBusySending :: ProcessState -- | The process is busy with killing ProcessBusySendingShutdown :: ProcessState -- | The process is busy with killing ProcessBusySendingInterrupt :: ProcessState -- | The process blocked by a receiveAnyMessage ProcessBusyReceiving :: ProcessState -- | The process blocked by a linkProcess ProcessBusyLinking :: ProcessState -- | The process blocked by a unlinkProcess ProcessBusyUnlinking :: ProcessState -- | The process blocked by a monitor ProcessBusyMonitoring :: ProcessState -- | The process blocked by a demonitor ProcessBusyDemonitoring :: ProcessState -- | The process was interrupted ProcessInterrupted :: ProcessState -- | The process was shutdown or crashed ProcessShuttingDown :: ProcessState -- | Use executeAndResumeOrExit to execute YieldProcess. -- Refer to YieldProcess for more information. yieldProcess :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Eff r () -- | Send a message to a process addressed by the ProcessId. See -- SendMessage. -- -- The message will be reduced to normal form (rnf) by/in the -- caller process. sendMessage :: forall r q o. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o, NFData o) => ProcessId -> o -> Eff r () -- | Send a Dynamic value to a process addressed by the -- ProcessId. See SendMessage. sendAnyMessage :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> StrictDynamic -> Eff r () -- | Exit a process addressed by the ProcessId. The process will -- exit, it might do some cleanup, but is ultimately unrecoverable. See -- SendShutdown. sendShutdown :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> Interrupt 'NoRecovery -> Eff r () -- | Interrupts a process addressed by the ProcessId. The process -- might exit, or it may continue. | Like sendInterrupt, but also -- return True iff the process to exit exists. sendInterrupt :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> Interrupt 'Recoverable -> Eff r () -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r Int -- | Receive and cast the message to some Typeable instance. See -- ReceiveSelectedMessage for more documentation. This will wait -- for a message of the return type using receiveSelectedMessage receiveMessage :: forall a r q. (HasCallStack, Typeable a, NFData a, Show a, SetMember Process (Process q) r, Member Interrupts r) => Eff r a -- | Block until a message was received, that is not Nothing after -- applying a callback to it. See ReceiveSelectedMessage for more -- documentation. receiveSelectedMessage :: forall r q a. (HasCallStack, Show a, SetMember Process (Process q) r, Member Interrupts r) => MessageSelector a -> Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r [StrictDynamic] -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r StrictDynamic -- | Like receiveSelectedLoop but refined to casting to a specific -- Typeable using selectMessage. receiveLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack, NFData a, Typeable a) => (Either (Interrupt 'Recoverable) a -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Enter a loop to receive messages and pass them to a callback, until -- the function returns Just a result. Only the messages of the -- given type will be received. If the process is interrupted by an -- exception of by a SendShutdown from another process, with an -- exit reason that satisfies isRecoverable, then the callback -- will be invoked with Left Interrupt, otherwise -- the process will be exited with the same reason using -- exitBecause. See also ReceiveSelectedMessage for more -- documentation. receiveSelectedLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => MessageSelector a -> (Either (Interrupt 'Recoverable) a -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Like receiveSelectedLoop but not selective. See also -- selectAnyMessage, receiveSelectedLoop. receiveAnyLoop :: forall r q endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => (Either (Interrupt 'Recoverable) StrictDynamic -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | A function that decided if the next message will be received by -- ReceiveSelectedMessage. It conveniently is an instance of -- Alternative so the message selector can be combined: > > -- selectInt :: MessageSelector Int > selectInt = selectMessage > -- > selectString :: MessageSelector String > selectString = -- selectMessage > > selectIntOrString :: MessageSelector (Either -- Int String) > selectIntOrString = > Left $ -- selectTimeout| Right $ selectString data MessageSelector a -- | Create a message selector for a value that can be obtained by -- fromStrictDynamic. selectMessage :: Typeable t => MessageSelector t -- | Create a message selector from a predicate. filterMessage :: Typeable a => (a -> Bool) -> MessageSelector a -- | Select a message of type a and apply the given function to -- it. If the function returns Just The -- ReceiveSelectedMessage function will return the result (sans -- Maybe). selectMessageWith :: Typeable a => (a -> Maybe b) -> MessageSelector b -- | Create a message selector. selectDynamicMessage :: (StrictDynamic -> Maybe a) -> MessageSelector a -- | Create a message selector that will match every message. This is -- lazy because the result is not forceed. selectAnyMessage :: MessageSelector StrictDynamic -- | Returns the ProcessId of the current process. self :: (HasCallStack, SetMember Process (Process q) r) => Eff r ProcessId -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r Bool -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. If the new -- process is interrupted, the process will Shutdown with the -- Interrupt wrapped in interruptToExit. For specific use -- cases it might be better to use spawnRaw. spawn :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r () -- | Start a new process, and immediately link to it. spawnLink :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. The spawned -- process has only the raw ConsProcess effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r () -- | Exit the process with a Interrupt. exitBecause :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => Interrupt 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => String -> Eff r a -- | Connect the calling process to another process, such that if one of -- the processes crashes (i.e. isCrash returns True), the -- other is shutdown with the Interrupt -- LinkedProcessCrashed. linkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. unlinkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Monitor another process. When the monitored process exits a -- ProcessDown is sent to the calling process. The return value is -- a unique identifier for that monitor. There can be multiple monitors -- on the same process, and a message for each will be sent. If the -- process is already dead, the ProcessDown message will be sent -- immediately, without exit reason monitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => MonitorReference -> Eff r () -- | A monitored process exited. This message is sent to a process by the -- scheduler, when a process that was monitored died. data ProcessDown ProcessDown :: !MonitorReference -> !SomeExitReason -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !SomeExitReason -- | A MessageSelector for the ProcessDown message of a -- specific process. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | Make an Interrupt for a ProcessDown message. -- -- For example: doSomething >>= either (interrupt . -- becauseProcessIsDown) return becauseProcessIsDown :: ProcessDown -> Interrupt 'Recoverable -- | A value that contains a unique reference of a process monitoring. data MonitorReference MonitorReference :: Int -> ProcessId -> MonitorReference [monitorIndex] :: MonitorReference -> Int [monitoredProcess] :: MonitorReference -> ProcessId -- | monitor another process before while performing an action and -- demonitor afterwards. withMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r, Typeable a, Show a) => ProcessId -> MessageSelector a -> Eff r (Either ProcessDown a) -- | Handle all Interrupts of an InterruptableProcess by -- wrapping them up in interruptToExit and then do a process -- Shutdown. provideInterruptsShutdown :: forall e a. Eff (InterruptableProcess e) a -> Eff (ConsProcess e) a -- | Handle Interrupts arising during process operations, e.g. when -- a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. handleInterrupts :: (HasCallStack, Member Interrupts r) => (Interrupt 'Recoverable -> Eff r a) -> Eff r a -> Eff r a -- | Like handleInterrupts, but instead of passing the -- Interrupt to a handler function, Either is returned. tryUninterrupted :: (HasCallStack, Member Interrupts r) => Eff r a -> Eff r (Either (Interrupt 'Recoverable) a) -- | Handle Interrupts arising during process operations, e.g. when -- a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. exitOnInterrupt :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r) => Eff r a -> Eff r a -- | Handle interrupts by logging them with logProcessExit and -- otherwise ignoring them. logInterrupts :: forall r. (Member Logs r, HasCallStack, Member Interrupts r) => Eff r () -> Eff r () -- | Handle Interrupts arising during process operations, e.g. when -- a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. provideInterrupts :: HasCallStack => Eff (Interrupts : r) a -> Eff r (Either (Interrupt 'Recoverable) a) -- | Wrap all (left) Interrupts into interruptToExit and -- return the (right) NoRecovery Interrupts as is. mergeEitherInterruptAndExitReason :: Either (Interrupt 'Recoverable) (Interrupt 'NoRecovery) -> Interrupt 'NoRecovery -- | Throw an Interrupt, can be handled by handleInterrupts -- or exitOnInterrupt or provideInterrupts. interrupt :: (HasCallStack, Member Interrupts r) => Interrupt 'Recoverable -> Eff r a -- | Execute a and action and return the result; if the process is -- interrupted by an error or exception, or an explicit shutdown from -- another process, or through a crash of a linked process, i.e. whenever -- the exit reason satisfies isRecoverable, return the exit -- reason. executeAndResume :: forall q r v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r (Either (Interrupt 'Recoverable) v) -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrExit :: forall r q v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrThrow :: forall q r v. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Process q (ResumeProcess v) -> Eff r v -- | A sum-type with reasons for why a process operation, such as receiving -- messages, is interrupted in the scheduling loop. -- -- This includes errors, that can occur when scheduling messages. data Interrupt (t :: ExitRecovery) -- | A process has finished a unit of work and might exit or work on -- something else. This is primarily used for interrupting infinite -- server loops, allowing for additional cleanup work before exiting -- (e.g. with ExitNormally) [NormalExitRequested] :: Interrupt 'Recoverable -- | A process that should be running was not running. [OtherProcessNotRunning] :: ProcessId -> Interrupt 'Recoverable -- | A Recoverable timeout has occurred. [TimeoutInterrupt] :: String -> Interrupt 'Recoverable -- | A linked process is down [LinkedProcessCrashed] :: ProcessId -> Interrupt 'Recoverable -- | An exit reason that has an error message and is Recoverable. [ErrorInterrupt] :: String -> Interrupt 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: Interrupt 'NoRecovery -- | An error causes the process to exit immediately. For example an -- unexpected runtime exception was thrown, i.e. an exception derived -- from SomeException Or a Recoverable Interrupt was not -- recoverd. [ExitUnhandledError] :: Text -> Interrupt 'NoRecovery -- | A process shall exit immediately, without any cleanup was cancelled -- (e.g. killed, in cancel) [ExitProcessCancelled] :: Interrupt 'NoRecovery -- | Return either ExitNormally or interruptToExit from a -- Recoverable Interrupt; -- -- If the Interrupt is NormalExitRequested then return -- ExitNormally interruptToExit :: Interrupt 'Recoverable -> Interrupt 'NoRecovery -- | This kind is used to indicate if a Interrupt can be treated -- like a short interrupt which can be handled or ignored. data ExitRecovery Recoverable :: ExitRecovery NoRecovery :: ExitRecovery -- | Interrupts which are Recoverable. type RecoverableInterrupt = Interrupt 'Recoverable -- | Exceptions containing Interrupts. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc (Interrupt 'Recoverable) -- | This adds a layer of the Interrupts effect on top of -- ConsProcess type InterruptableProcess e = Interrupts : ConsProcess e -- | This value indicates whether a process exited in way consistent with -- the planned behaviour or not. data ExitSeverity NormalExit :: ExitSeverity Crash :: ExitSeverity -- | An existential wrapper around Interrupt data SomeExitReason [SomeExitReason] :: Interrupt x -> SomeExitReason -- | Get the ExitRecovery toExitRecovery :: Interrupt r -> ExitRecovery -- | A predicate for recoverable exit reasons. This predicate defines the -- exit reasons which functions such as executeAndResume isRecoverable :: Interrupt x -> Bool -- | Get the ExitSeverity of a Interrupt. toExitSeverity :: Interrupt e -> ExitSeverity -- | A predicate for linked process crashes. isProcessDownInterrupt :: Maybe ProcessId -> Interrupt r -> Bool -- | A predicate for crashes. A crash happens when a process exits -- with an Interrupt other than ExitNormally isCrash :: Interrupt x -> Bool -- | Print a Interrupt to Just a formatted String when -- isCrash is True. This can be useful in combination with -- view patterns, e.g.: -- --
--   logCrash :: Interrupt -> Eff e ()
--   logCrash (toCrashReason -> Just reason) = logError reason
--   logCrash _ = return ()
--   
-- -- Though this can be improved to: -- --
--   logCrash = traverse_ logError . toCrashReason
--   
toCrashReason :: Interrupt x -> Maybe Text -- | Partition a SomeExitReason back into either a NoRecovery -- or a Recoverable Interrupt fromSomeExitReason :: SomeExitReason -> Either (Interrupt 'NoRecovery) (Interrupt 'Recoverable) -- | Log the Interrupts logProcessExit :: forall e x. (Member Logs e, HasCallStack) => Interrupt x -> Eff e () instance GHC.Show.Show v => GHC.Show.Show (Control.Eff.Concurrent.Process.ResumeProcess v) instance GHC.Generics.Generic1 Control.Eff.Concurrent.Process.ResumeProcess instance GHC.Generics.Generic (Control.Eff.Concurrent.Process.ResumeProcess v) instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessDown instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessDown instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ProcessDown instance GHC.Generics.Generic Control.Eff.Concurrent.Process.MonitorReference instance GHC.Classes.Ord Control.Eff.Concurrent.Process.MonitorReference instance GHC.Classes.Eq Control.Eff.Concurrent.Process.MonitorReference instance GHC.Read.Read Control.Eff.Concurrent.Process.MonitorReference instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessId instance GHC.Real.Real Control.Eff.Concurrent.Process.ProcessId instance GHC.Real.Integral Control.Eff.Concurrent.Process.ProcessId instance GHC.Enum.Enum Control.Eff.Concurrent.Process.ProcessId instance GHC.Num.Num Control.Eff.Concurrent.Process.ProcessId instance GHC.Enum.Bounded Control.Eff.Concurrent.Process.ProcessId instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessId instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessId instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ExitSeverity instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ExitSeverity instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ExitSeverity instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ExitRecovery instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ExitRecovery instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ExitRecovery instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ProcessState instance GHC.Enum.Enum Control.Eff.Concurrent.Process.ProcessState instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessState instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessState instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessState instance GHC.Read.Read Control.Eff.Concurrent.Process.ProcessState instance GHC.Base.Functor Control.Eff.Concurrent.Process.MessageSelector instance GHC.Base.Semigroup a => GHC.Base.Monoid (Control.Eff.Concurrent.Process.MessageSelector a) instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Control.Eff.Concurrent.Process.MessageSelector a) instance GHC.Show.Show (Control.Eff.Concurrent.Process.Process r b) instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Control.Eff.Concurrent.Process.ResumeProcess a) instance Control.DeepSeq.NFData1 Control.Eff.Concurrent.Process.ResumeProcess instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessDown instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessDown instance GHC.Classes.Ord Control.Eff.Concurrent.Process.SomeExitReason instance GHC.Classes.Eq Control.Eff.Concurrent.Process.SomeExitReason instance GHC.Show.Show Control.Eff.Concurrent.Process.SomeExitReason instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.SomeExitReason instance GHC.Show.Show (Control.Eff.Concurrent.Process.Interrupt x) instance GHC.Exception.Type.Exception (Control.Eff.Concurrent.Process.Interrupt 'Control.Eff.Concurrent.Process.Recoverable) instance GHC.Exception.Type.Exception (Control.Eff.Concurrent.Process.Interrupt 'Control.Eff.Concurrent.Process.NoRecovery) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Process.Interrupt x) instance GHC.Classes.Ord (Control.Eff.Concurrent.Process.Interrupt x) instance GHC.Classes.Eq (Control.Eff.Concurrent.Process.Interrupt x) instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.MonitorReference instance GHC.Show.Show Control.Eff.Concurrent.Process.MonitorReference instance GHC.Read.Read Control.Eff.Concurrent.Process.ProcessId instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessId instance GHC.Show.Show Control.Eff.Concurrent.Process.ExitSeverity instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ExitSeverity instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ExitRecovery instance GHC.Show.Show Control.Eff.Concurrent.Process.ExitRecovery instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessState instance Data.Default.Class.Default Control.Eff.Concurrent.Process.ProcessState instance GHC.Base.Applicative Control.Eff.Concurrent.Process.MessageSelector instance GHC.Base.Alternative Control.Eff.Concurrent.Process.MessageSelector instance GHC.Show.Show Control.Eff.Concurrent.Process.StrictDynamic -- | Functions for timeouts when receiving messages. -- -- NOTE: If you use a single threaded scheduler, these functions will not -- work as expected. (This is an open TODO) module Control.Eff.Concurrent.Process.Timer -- | A number of micro seconds. newtype Timeout TimeoutMicros :: Int -> Timeout [fromTimeoutMicros] :: Timeout -> Int -- | The reference to a timer started by startTimer, required to -- stop a timer via cancelTimer. data TimerReference -- | A value to be sent when timer started with startTimer has -- elapsed. data TimerElapsed -- | Send a message to a given process after waiting. The message is -- created by applying the function parameter to the -- TimerReference, such that the message can directly refer to the -- timer. sendAfter :: forall r q message. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable message, NFData message) => ProcessId -> Timeout -> (TimerReference -> message) -> Eff r TimerReference -- | Start a new timer, after the time has elapsed, TimerElapsed is -- sent to calling process. The message also contains the -- TimerReference returned by this function. Use -- cancelTimer to cancel the timer. Use selectTimerElapsed -- to receive the message using receiveSelectedMessage. To receive -- messages with guarded with a timeout see receiveAfter. startTimer :: forall r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Timeout -> Eff r TimerReference -- | Cancel a timer started with startTimer. cancelTimer :: forall r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => TimerReference -> Eff r () -- | A MessageSelector matching TimerElapsed messages created -- by startTimer. selectTimerElapsed :: TimerReference -> MessageSelector TimerElapsed -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfter. receiveAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable a, NFData a, Show a) => Timeout -> Eff r (Maybe a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Left TimerElapsed. This -- is based on selectTimerElapsed and startTimer. receiveSelectedAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Show a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitor combined with -- receiveSelectedAfter. receiveSelectedWithMonitorAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Show a) => ProcessId -> MessageSelector a -> Timeout -> Eff r (Either (Either ProcessDown TimerElapsed) a) instance GHC.Classes.Eq Control.Eff.Concurrent.Process.Timer.TimerElapsed instance GHC.Classes.Ord Control.Eff.Concurrent.Process.Timer.TimerElapsed instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.Timer.TimerElapsed instance GHC.Enum.Enum Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Real.Real Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Real.Integral Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Num.Num Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Classes.Eq Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Classes.Ord Control.Eff.Concurrent.Process.Timer.TimerReference instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Enum.Enum Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Real.Real Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Real.Integral Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Num.Num Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Classes.Eq Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Classes.Ord Control.Eff.Concurrent.Process.Timer.Timeout instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Show.Show Control.Eff.Concurrent.Process.Timer.TimerElapsed instance GHC.Show.Show Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Show.Show Control.Eff.Concurrent.Process.Timer.Timeout -- | This module contains a mechanism to specify what kind of messages (aka -- requests) a Server (Process) can handle, and if -- the caller blocks and waits for an answer, which the server process -- provides. -- -- The type magic in the Api type family allows to define a -- related set of requests along with the corresponding responses. -- -- Request handling can be either blocking, if a response is required, or -- non-blocking. -- -- A process can serve a specific Api instance by using the -- functions provided by the Control.Eff.Concurrent.Api.Server -- module. -- -- To enable a process to use such a service, the functions -- provided by the Control.Eff.Concurrent.Api.Client should be -- used. module Control.Eff.Concurrent.Api -- | This data family defines an API, a communication interface description -- between at least two processes. The processes act as servers or -- client(s) regarding a specific instance of this type. -- -- The first parameter is usually a user defined phantom type that -- identifies the Api instance. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Api instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Also, for better logging, the an instance of ToPretty for the -- Api index type must be given. -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   data instance Api BookShop r where
--     RentBook  :: BookId   -> Api BookShop ('Synchronous (Either RentalError RentalId))
--     BringBack :: RentalId -> Api BookShop 'Asynchronous
--   
--   type instance ToPretty BookShop = PutStr "book shop"
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
data family Api (api :: Type) (reply :: Synchronicity) -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Api -- instance. data Synchronicity -- | Specify that handling a request is a blocking operation with a -- specific return type, e.g. ('Synchronous (Either RentalError -- RentalId)) Synchronous :: Type -> Synchronicity -- | Non-blocking, asynchronous, request handling Asynchronous :: Synchronicity -- | A set of constraints for types that can evaluated via NFData, -- compared via Ord and presented dynamically via Typeable, -- and represented both as values via Show, as well as on the type -- level via ToPretty. type Tangible i = (NFData i, Typeable i, Show i, PrettyTypeShow (ToPretty i)) -- | This is a tag-type that wraps around a ProcessId and holds an -- Api index type. newtype Server api Server :: ProcessId -> Server api [_fromServer] :: Server api -> ProcessId fromServer :: forall api_aXxm api_aXVF. Iso (Server api_aXxm) (Server api_aXVF) ProcessId ProcessId -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API proxyAsServer :: proxy api -> ProcessId -> Server api -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API asServer :: forall api. ProcessId -> Server api instance forall k (api :: k). Control.DeepSeq.NFData (Control.Eff.Concurrent.Api.Server api) instance forall k (api :: k). GHC.Classes.Ord (Control.Eff.Concurrent.Api.Server api) instance forall k (api :: k). GHC.Classes.Eq (Control.Eff.Concurrent.Api.Server api) instance forall k (api :: k). Data.Type.Pretty.PrettyTypeShow (Data.Type.Pretty.ToPretty api) => GHC.Show.Show (Control.Eff.Concurrent.Api.Server api) -- | Logging via extensible-effects -- -- Logging consist of two effects: -- -- -- --

Example

-- --
--   exampleLogging :: IO ()
--   exampleLogging =
--       runLift
--     $ withLogging consoleLogWriter
--     $ do
--         logDebug "test 1.1"
--         logError "test 1.2"
--         censorLogs (prefixLogMessagesWith "NESTED: ")
--          $ do
--               addLogWriter debugTraceLogWriter
--                $ setLogPredicate (\m -> (view lmMessage m) /= "not logged")
--                $ do
--                     logInfo "not logged"
--                     logMsg "test 2.1"
--               logWarning "test 2.2"
--         logCritical "test 1.3"
--   
-- --

Log Message Data Type

-- -- A singular logging event is contained in a -- LogMessages value. -- -- The LogMessage is modelled along RFC-5424. -- -- There is the ToLogMessage class for converting to -- LogMessage. Although the author is not clear on how to -- pursue the approach. -- --

Receiving and Filtering

-- -- LogMessages are sent using logMsg and friends, see -- Control.Eff.Log#SendingLogs -- --

Log Message Predicates

-- -- There is a single global LogPredicate that can be used to -- suppress logs before they are passed to any LogWriter. -- -- This is done by the logMsg function. -- -- Also, LogMessages are evaluated using deepseq, -- after they pass the LogPredicate, also inside -- logMsg. -- -- See Control.Eff.Log#LogPredicate -- --

Writing and Rendering

-- -- Writing is done through a LogWriter; the current -- LogWriter value to use is held by the LogWriterReader -- effect. -- --

Log Message Rendering

-- -- Message are rendered by LogMessageRenderers found in the -- Control.Eff.Log.MessageRenderer. -- --

LogWriters

-- -- module Control.Eff.Log -- | Log a message. -- -- All logging goes through this function. -- -- This function is the only place where the LogPredicate is -- applied. -- -- Also, LogMessages are evaluated using deepseq, -- after they pass the LogPredicate. logMsg :: forall e. (HasCallStack, Member Logs e) => LogMessage -> Eff e () -- | Log a Text as LogMessage with a given Severity. logWithSeverity :: forall e. (HasCallStack, Member Logs e) => Severity -> Text -> Eff e () -- | Log a Text as LogMessage with a given Severity. logWithSeverity' :: forall e. (HasCallStack, Member Logs e) => Severity -> String -> Eff e () -- | Log a String as emergencySeverity. logEmergency :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a String as emergencySeverity. logEmergency' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a message with alertSeverity. logAlert :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a message with alertSeverity. logAlert' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a criticalSeverity message. logCritical :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a criticalSeverity message. logCritical' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a errorSeverity message. logError :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a errorSeverity message. logError' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a warningSeverity message. logWarning :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a warningSeverity message. logWarning' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a noticeSeverity message. logNotice :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a noticeSeverity message. logNotice' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a informationalSeverity message. logInfo :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a informationalSeverity message. logInfo' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Log a debugSeverity message. logDebug :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a debugSeverity message. logDebug' :: forall e. (HasCallStack, Member Logs e) => String -> Eff e () -- | Include LogMessages that match a LogPredicate. -- -- excludeLogMessages p allows log message to be logged if p -- m -- -- Although it is enough if the previous predicate holds. See -- excludeLogMessages and modifyLogPredicate. -- -- See Control.Eff.Log#LogPredicate includeLogMessages :: forall e a. Member Logs e => LogPredicate -> Eff e a -> Eff e a -- | Exclude LogMessages that match a LogPredicate. -- -- excludeLogMessages p discards logs if p m -- -- Also the previous predicate must also hold for a message to be logged. -- See excludeLogMessages and modifyLogPredicate. -- -- See Control.Eff.Log#LogPredicate excludeLogMessages :: forall e a. Member Logs e => LogPredicate -> Eff e a -> Eff e a -- | Keep only those messages, for which a predicate holds. -- -- E.g. to keep only messages which begin with OMG: -- --
--   exampleLogPredicate :: IO Int
--   exampleLogPredicate =
--       runLift
--     $ withLogging consoleLogWriter
--     $ do logMsg "test"
--          setLogPredicate (\ msg -> case view lmMessage msg of
--                                     'O':'M':'G':_ -> True
--                                     _             -> False)
--                            (do logMsg "this message will not be logged"
--                                logMsg "OMG logged"
--                                return 42)
--   
-- -- In order to also delegate to the previous predicate, use -- modifyLogPredicate -- -- See Control.Eff.Log#LogPredicate setLogPredicate :: forall r b. (Member Logs r, HasCallStack) => LogPredicate -> Eff r b -> Eff r b -- | Change the LogPredicate. -- -- Other than setLogPredicate this function allows to include the -- previous predicate, too. -- -- For to discard all messages currently no satisfying the predicate and -- also all messages that are to long: -- --
--   modifyLogPredicate (previousPredicate msg -> previousPredicate msg && length (lmMessage msg) < 29 )
--                      (do logMsg "this message will not be logged"
--                          logMsg "this message might be logged")
--   
-- -- See Control.Eff.Log#LogPredicate modifyLogPredicate :: forall e b. (Member Logs e, HasCallStack) => (LogPredicate -> LogPredicate) -> Eff e b -> Eff e b -- | Get the current Logs filter/transformer function. -- -- See Control.Eff.Log#LogPredicate askLogPredicate :: forall e. Member Logs e => Eff e LogPredicate -- | Replace the current LogWriter. To add an additional log message -- consumer use addLogWriter setLogWriter :: forall h e a. LogsTo h e => LogWriter h -> Eff e a -> Eff e a -- | Combine the effects of a given LogWriter and the existing one. -- --
--   import Data.Text    as T
--   import Data.Text.IO as T
--   
--   exampleAddLogWriter :: IO ()
--   exampleAddLogWriter = go >>= T.putStrLn
--    where go = fmap (unlines . map renderLogMessageConsoleLog . snd)
--                 $  runLift
--                 $  runCaptureLogWriter
--                 $  withLogging captureLogWriter
--                 $  addLogWriter (mappingLogWriter (lmMessage %~ ("CAPTURED "++)) captureLogWriter)
--                 $  addLogWriter (filteringLogWriter severeMessages (mappingLogWriter (lmMessage %~ ("TRACED "++)) debugTraceLogWriter))
--                 $  do
--                       logEmergency "test emergencySeverity 1"
--                       logCritical "test criticalSeverity 2"
--                       logAlert "test alertSeverity 3"
--                       logError "test errorSeverity 4"
--                       logWarning "test warningSeverity 5"
--                       logInfo "test informationalSeverity 6"
--                       logDebug "test debugSeverity 7"
--          severeMessages = view (lmSeverity . to (<= errorSeverity))
--   
addLogWriter :: forall h e a. (HasCallStack, LogsTo h e, Monad h) => LogWriter h -> Eff e a -> Eff e a -- | Change the current LogWriter. modifyLogWriter :: forall h e a. LogsTo h e => (LogWriter h -> LogWriter h) -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriter censorLogs :: LogsTo h e => (LogMessage -> LogMessage) -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression, -- as in censorLogs but with a effectful function. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriterM censorLogsM :: (LogsTo h e, Monad h) => (LogMessage -> h LogMessage) -> Eff e a -> Eff e a -- | This effect sends LogMessages and is a reader for a -- LogPredicate. -- -- Logs are sent via logMsg; for more information about log -- predicates, see Control.Eff.Log#LogPredicate -- -- This effect is handled via withLogging. data Logs v -- | A constraint alias for effects that requires a LogWriterReader, -- as well as that the contained LogWriterReader has a -- HandleLogWriter instance. -- -- The requirements of this constraint are provided by: -- -- type LogsTo h e = (Member Logs e, HandleLogWriter h, LogWriterEffects h <:: e, SetMember LogWriterReader (LogWriterReader h) e) -- | Handle the Logs and LogWriterReader effects. -- -- It installs the given LogWriter, which determines the -- underlying LogWriter type parameter. -- -- Example: -- --
--   exampleWithLogging :: IO ()
--   exampleWithLogging =
--       runLift
--     $ withLogging consoleLogWriter
--     $ logDebug "Oh, hi there"
--   
withLogging :: forall h e a. (Applicative h, LogsTo h (Logs : (LogWriterReader h : e))) => LogWriter h -> Eff (Logs : (LogWriterReader h : e)) a -> Eff e a -- | Handles the Logs and LogWriterReader effects. -- -- By default it uses the noOpLogWriter, but using -- setLogWriter the LogWriter can be replaced. -- -- This is like withLogging applied to noOpLogWriter -- -- Example: -- --
--   exampleWithSomeLogging :: ()
--   exampleWithSomeLogging =
--       run
--     $ withSomeLogging @PureLogWriter
--     $ logDebug "Oh, hi there"
--   
withSomeLogging :: forall h e a. (Applicative h, LogsTo h (Logs : (LogWriterReader h : e))) => Eff (Logs : (LogWriterReader h : e)) a -> Eff e a -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withLogging. runLogs :: forall h e b. LogsTo h (Logs : e) => LogPredicate -> Eff (Logs : e) b -> Eff e b -- | Consume log messages. -- -- Exposed for custom extensions, if in doubt use withLogging. -- -- Respond to all LogMessages logged from the given action, up to -- any MonadBaseControl liftings. -- -- Note that all logging is done through logMsg and that means -- only messages passing the LogPredicate are received. -- -- The LogMessages are consumed once they are passed to the -- given callback function, previous respondToLogMessage -- invocations further up in the call stack will not get the messages -- anymore. -- -- Use interceptLogMessages if the messages shall be passed any -- previous handler. -- -- NOTE: The effects of this function are lost when using -- MonadBaseControl, MonadMask, MonadCatch and -- MonadThrow. -- -- In contrast the functions based on modifying the LogWriter, -- such as addLogWriter or censorLogs, are save to use in -- combination with the aforementioned liftings. respondToLogMessage :: forall r b. Member Logs r => (LogMessage -> Eff r ()) -> Eff r b -> Eff r b -- | Change the LogMessages using an effectful function. -- -- Exposed for custom extensions, if in doubt use withLogging. -- -- This differs from respondToLogMessage in that the intercepted -- messages will be written either way, albeit in altered form. -- -- NOTE: The effects of this function are lost when using -- MonadBaseControl, MonadMask, MonadCatch and -- MonadThrow. -- -- In contrast the functions based on modifying the LogWriter, -- such as addLogWriter or censorLogs, are save to use in -- combination with the aforementioned liftings. interceptLogMessages :: forall r b. Member Logs r => (LogMessage -> Eff r LogMessage) -> Eff r b -> Eff r b -- | Support code to implement Api _server_ processes. module Control.Eff.Concurrent.Api.Server -- | Serve an Api in a newly spawned process. spawnApiServer :: forall api eff. (ToServerPids api, HasCallStack, Member Logs eff, PrettyTypeShow (PrettyServerPids api)) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Serve an Api in a newly spawned -and linked - process. spawnLinkApiServer :: forall api eff. (ToServerPids api, HasCallStack, Member Logs eff, Typeable api, PrettyTypeShow (PrettyServerPids api)) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; the callbacks -- have access to a state value. -- -- The initial state value is returned by the action given as parameter, -- from within the new process. spawnApiServerStateful :: forall api eff state. (HasCallStack, ToServerPids api, NFData state, PrettyTypeShow (PrettyServerPids api)) => Eff (InterruptableProcess eff) state -> MessageCallback api (State state : InterruptableProcess eff) -> InterruptCallback (State state : ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. spawnApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff, Member Logs serverEff, PrettyTypeShow (PrettyServerPids api)) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. Links to the calling process like linkProcess would. spawnLinkApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff, Member Logs serverEff, PrettyTypeShow (PrettyServerPids api)) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | Receive loop for Api calls. This starts a receive loop -- for a MessageCallback. It is used behind the scenes by -- spawnLinkApiServerEffectful and spawnApiServerEffectful. apiServerLoop :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff, Member Logs serverEff, PrettyTypeShow (PrettyServerPids api)) => MessageCallback api serverEff -> InterruptCallback serverEff -> Eff serverEff () -- | A command to the server loop started by apiServerLoop. -- Typically returned by a MessageCallback to indicate if the -- server should continue or stop. data CallbackResult (r :: ExitRecovery) -- | Tell the server to keep the server loop running [AwaitNext] :: CallbackResult r -- | Tell the server to exit, this will cause apiServerLoop to stop -- handling requests without exiting the process. [StopServer] :: Interrupt r -> CallbackResult r -- | An existential wrapper around a MessageSelector and a function -- that handles the selected message. The api type parameter is -- a phantom type. -- -- The return value of the handler function is a CallbackResult. data MessageCallback api eff [MessageCallback] :: MessageSelector a -> (a -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleCasts :: forall api eff. (HasCallStack, Typeable api, Typeable (Api api 'Asynchronous), NFData (Request api)) => (Api api 'Asynchronous -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks -- --

Example

-- --
--   handleCalls
--     ( (RentBook bookId customerId) runCall ->
--        runCall $ do
--            rentalIdE <- rentBook bookId customerId
--            case rentalIdE of
--              -- on fail we just don't send a reply, let the caller run into
--              -- timeout
--              Left err -> return (Nothing, AwaitNext)
--              Right rentalId -> return (Just rentalId, AwaitNext))
--   
handleCalls :: forall api eff effScheduler. (HasCallStack, Typeable api, SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (forall secret reply. (NFData reply, Typeable reply, Typeable (Api api ( 'Synchronous reply))) => Api api ( 'Synchronous reply) -> (Eff eff (Maybe reply, CallbackResult 'Recoverable) -> secret) -> secret) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleCastsAndCalls :: forall api eff effScheduler. (HasCallStack, Typeable api, Typeable (Api api 'Asynchronous), NFData (Request api), SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (Api api 'Asynchronous -> Eff eff (CallbackResult 'Recoverable)) -> (forall secret reply. (Typeable reply, Typeable (Api api ( 'Synchronous reply)), NFData reply) => Api api ( 'Synchronous reply) -> (Eff eff (Maybe reply, CallbackResult 'Recoverable) -> secret) -> secret) -> MessageCallback api eff -- | A variation of handleCalls that allows to defer a reply to a -- call. handleCallsDeferred :: forall api eff effScheduler. (HasCallStack, Typeable api, SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (forall reply. (Typeable reply, NFData reply, Typeable (Api api ( 'Synchronous reply))) => RequestOrigin (Api api ( 'Synchronous reply)) -> Api api ( 'Synchronous reply) -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleMessages :: forall eff a. (HasCallStack, NFData a, Typeable a) => (a -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleSelectedMessages :: forall eff a. HasCallStack => MessageSelector a -> (a -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleAnyMessages :: forall eff. HasCallStack => (StrictDynamic -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleProcessDowns :: forall eff. HasCallStack => (MonitorReference -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | A fallbackHandler that drops the left-over messages. dropUnhandledMessages :: forall eff. HasCallStack => MessageCallback '[] eff -- | A fallbackHandler that terminates if there are unhandled -- messages. exitOnUnhandled :: forall eff. HasCallStack => MessageCallback '[] eff -- | A fallbackHandler that drops the left-over messages. logUnhandledMessages :: forall eff. (Member Logs eff, HasCallStack) => MessageCallback '[] eff -- | Compose two Apis to a type-level pair of them. -- --
--   handleCalls api1calls ^: handleCalls api2calls ^:
--   
(^:) :: forall (api1 :: Type) (apis2 :: [Type]) eff. HasCallStack => MessageCallback api1 eff -> MessageCallback apis2 eff -> MessageCallback (api1 : apis2) eff infixr 5 ^: -- | Make a fallback handler, i.e. a handler to which no other can be -- composed to from the right. fallbackHandler :: forall api eff. HasCallStack => MessageCallback api eff -> MessageCallback '[] eff -- | Helper type class for the return values of spawnApiServer et -- al. class ToServerPids (t :: k) where { type family PrettyServerPids t :: PrettyType; type family ServerPids t; } toServerPids :: ToServerPids t => proxy t -> ProcessId -> ServerPids t -- | Just a wrapper around a function that will be applied to the result of -- a MessageCallbacks StopServer clause, or an -- Interrupt caught during the execution of receive or a -- MessageCallback data InterruptCallback eff [InterruptCallback] :: (Interrupt 'Recoverable -> Eff eff (CallbackResult 'NoRecovery)) -> InterruptCallback eff -- | A smart constructor for InterruptCallbacks stopServerOnInterrupt :: forall eff. HasCallStack => InterruptCallback eff instance Data.Default.Class.Default (Control.Eff.Concurrent.Api.Server.InterruptCallback eff) instance Control.Eff.Concurrent.Api.Server.ToServerPids '[] instance (Control.Eff.Concurrent.Api.Server.ToServerPids api1, Control.Eff.Concurrent.Api.Server.ToServerPids api2) => Control.Eff.Concurrent.Api.Server.ToServerPids (api1 : api2) instance Control.Eff.Concurrent.Api.Server.ToServerPids api1 => Control.Eff.Concurrent.Api.Server.ToServerPids api1 instance forall k (api :: k) (eff :: [* -> *]). GHC.Base.Semigroup (Control.Eff.Concurrent.Api.Server.MessageCallback api eff) instance forall k (api :: k) (eff :: [* -> *]). GHC.Base.Monoid (Control.Eff.Concurrent.Api.Server.MessageCallback api eff) instance forall k (api :: k) (eff :: [* -> *]). Data.Default.Class.Default (Control.Eff.Concurrent.Api.Server.MessageCallback api eff) -- | A better, more safe implementation of the Erlang/OTP gen_server -- behaviour. **PLANNED TODO** @since 0.24.0 module Control.Eff.Concurrent.Api.GenServer -- | A type class for Api values that have an implementation which -- handles the Api. class (Typeable (GenServerState a), NFData (GenServerState a)) => GenServer a eff where { type family GenServerState a :: Type; } genServerInit :: (GenServer a eff, '[Interrupts, Logs] <:: eff, SetMember Process (Process q) eff) => a -> Eff eff (GenServerState a) genServerHandle :: (GenServer a eff, '[Interrupts, Logs] <:: eff, SetMember Process (Process q) eff) => Api a v -> Eff (State (GenServerState a) : eff) (ApiReply v) genServerInterrupt :: (GenServer a eff, '[Interrupts, Logs] <:: eff, SetMember Process (Process q) eff) => Interrupt 'Recoverable -> Eff (State (GenServerState a) : eff) () genServerInfoCommand :: GenServer a eff => Api a ( 'Synchronous Text) type family ApiReply (s :: Synchronicity) data SomeMessage a MkSomeMessage :: SomeMessage a runGenServer :: forall q e h a. (GenServer a e, SetMember Process (Process q) e, Member Interrupts e, LogsTo h e) => a -> Eff e (Server a) -- | Functions for Api clients. -- -- This modules is required to write clients that consume an Api. module Control.Eff.Concurrent.Api.Client -- | Send an Api request that has no return value and return as fast -- as possible. The type signature enforces that the corresponding -- Api clause is Asynchronous. The operation never fails, -- if it is important to know if the message was delivered, use -- call instead. -- -- The message will be reduced to normal form (rnf) in the caller -- process. cast :: forall r q o. (HasCallStack, SetMember Process (Process q) r, PrettyTypeShow (ToPretty o), Member Interrupts r, Typeable o, Typeable (Api o 'Asynchronous), NFData (Api o 'Asynchronous)) => Server o -> Api o 'Asynchronous -> Eff r () -- | Send an Api request and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Api clause -- is Synchronous. -- -- Always prefer callWithTimeout over call call :: forall result api r q. (SetMember Process (Process q) r, Member Interrupts r, Typeable api, PrettyTypeShow (ToPretty api), Typeable (Api api ( 'Synchronous result)), NFData (Api api ( 'Synchronous result)), Typeable result, NFData result, Show result, HasCallStack) => Server api -> Api api ( 'Synchronous result) -> Eff r result -- | Send an Api request and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Api clause -- is Synchronous. -- -- If the server that was called dies, this function interrupts the -- process with ProcessDown. If the server takes longer to reply -- than the given timeout, this function interrupts the process with -- TimeoutInterrupt. -- -- Always prefer this function over call callWithTimeout :: forall result api r q. (SetMember Process (Process q) r, Member Interrupts r, Typeable api, Typeable (Api api ( 'Synchronous result)), NFData (Api api ( 'Synchronous result)), Typeable result, NFData result, Show result, Member Logs r, Lifted IO q, Lifted IO r, HasCallStack, PrettyTypeShow (ToPretty api)) => Server api -> Api api ( 'Synchronous result) -> Timeout -> Eff r result -- | Like cast but take the Server from the reader provided -- by registerServer. castRegistered :: (Typeable o, ServesApi o r q, HasCallStack, Member Interrupts r, NFData (Api o 'Asynchronous)) => Api o 'Asynchronous -> Eff r () -- | Like call but take the Server from the reader provided -- by registerServer. callRegistered :: (Typeable reply, ServesApi o r q, HasCallStack, NFData reply, Show reply, NFData (Api o ( 'Synchronous reply)), Member Interrupts r) => Api o ( 'Synchronous reply) -> Eff r reply -- | Instead of passing around a Server value and passing to -- functions like cast or call, a Server can -- provided by a Reader effect, if there is only a single -- server for a given Api instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Server. type ServesApi o r q = (Typeable o, PrettyTypeShow (ToPretty o), SetMember Process (Process q) r, Member (ServerReader o) r) -- | The reader effect for ProcessIds for Apis, see -- registerServer type ServerReader o = Reader (Server o) -- | Get the Server registered with registerServer. whereIsServer :: Member (ServerReader o) e => Eff e (Server o) -- | Run a reader effect that contains the one server handling a -- specific Api instance. registerServer :: HasCallStack => Server o -> Eff (ServerReader o : r) a -> Eff r a -- | This module provides support for executing Process actions from -- IO. -- -- One use case is interacting with processes from the REPL, e.g.: -- --
--   >>> import Control.Eff.Concurrent.Process.SingleThreadedScheduler (defaultMain)
--   
-- --
--   >>> import Control.Eff.Loop
--   
-- --
--   >>> import Data.Dynamic
--   
-- --
--   >>> import Data.Maybe
--   
-- --
--   >>> s <- forkInteractiveScheduler Control.Eff.Concurrent.Process.SingleThreadedScheduler.defaultMain
--   
-- --
--   >>> fooPid <- submit s (spawn (foreverCheap (receiveAnyMessage SP >>= (logMsg . fromMaybe "Huh!??" . fromDynamic))))
--   
-- --
--   >>> fooPid
--   <0.1.0>
--   
-- --
--   >>> submit s (sendMessageAs SP fooPid "test")
--   test
--   
-- --
--   >>> submit s (sendShutdown SP fooPid)
--   
module Control.Eff.Concurrent.Process.Interactive -- | Contains the communication channels to interact with a scheduler -- running in its' own thread. data SchedulerSession r -- | Fork a scheduler with a process that communicates with it via -- MVar, which is also the reason for the Lift IO -- constraint. forkInteractiveScheduler :: forall r. SetMember Lift (Lift IO) r => (Eff (InterruptableProcess r) () -> IO ()) -> IO (SchedulerSession r) -- | Exit the scheduler immediately using an asynchronous exception. killInteractiveScheduler :: SchedulerSession r -> IO () -- | Send a Process effect to the main process of a scheduler, this -- blocks until the effect is executed. submit :: forall r a. SetMember Lift (Lift IO) r => SchedulerSession r -> Eff (InterruptableProcess r) a -> IO a -- | Combination of submit and cast. submitCast :: forall o r. (SetMember Lift (Lift IO) r, Typeable o, PrettyTypeShow (ToPretty o), NFData (Api o 'Asynchronous), Member Interrupts r) => SchedulerSession r -> Server o -> Api o 'Asynchronous -> IO () -- | Combination of submit and cast. submitCall :: forall o q r. (SetMember Lift (Lift IO) r, Typeable o, PrettyTypeShow (ToPretty o), Typeable q, NFData q, Show q, Member Interrupts r, NFData (Api o ( 'Synchronous q))) => SchedulerSession r -> Server o -> Api o ( 'Synchronous q) -> IO q -- | A process supervisor spawns and monitors child processes. -- -- The child processes are mapped to symbolic identifier values: -- Child-IDs. -- -- This is the barest, most minimal version of a supervisor. Children can -- be started, but not restarted. -- -- Children can efficiently be looked-up by an id-value, and when the -- supervisor is shutdown, all children will be shutdown these are -- actually all the features of this supervisor implementation. -- -- Also, this minimalist supervisor only knows how to spawn a single kind -- of child process. -- -- When a supervisor spawns a new child process, it expects the child -- process to return a ProcessId. The supervisor will -- monitor the child process. -- -- This is in stark contrast to how Erlang/OTP handles things; In the OTP -- Supervisor, the child has to link to the parent. This allows the child -- spec to be more flexible in that no pid has to be passed from -- the child start function to the supervisor process, and also, a child -- may break free from the supervisor by unlinking. -- -- Now while this seems nice at first, this might actually cause -- surprising results, since it is usually expected that stopping a -- supervisor also stops the children, or that a child exit shows up in -- the logging originating from the former supervisor. -- -- The approach here is to allow any child to link to the supervisor to -- realize when the supervisor was violently killed, and otherwise give -- the child no chance to unlink itself from its supervisor. -- -- This module is far simpler than the Erlang/OTP counter part, of a -- simple_one_for_one supervisor. -- -- The future of this supervisor might not be a-lot more than it -- currently is. The ability to restart processes might be implemented -- outside of this supervisor module. -- -- One way to do that is to implement the restart logic in a seperate -- module, since the child-id can be reused when a child exits. module Control.Eff.Concurrent.Api.Supervisor -- | Api type for supervisor processes. -- -- The supervisor process contains a SpawnFun from which it -- can spawn new child processes. -- -- The supervisor maps an identifier value of type childId to a -- ProcessId and a spawnResult type. -- -- This spawnResult is likely a tuple of Server process -- ids that allow type-safe interaction with the process. -- -- Also, this serves as handle or reference for interacting with a -- supervisor process. -- -- A value of this type is returned by startSupervisor data Sup childId spawnResult -- | A function that will initialize the child process. -- -- The process-id returned from this function is kept private, but it is -- possible to return '(ProcessId, ProcessId)' for example. -- -- The function is expected to return a value - usually a tuple of -- Server values for the different aspects of a process, such as -- sending API requests, managing observers and receiving other auxiliary -- API messages. type SpawnFun i e o = i -> Eff e (o, ProcessId) -- | Options that control the 'Sup i o' process. -- -- This contains: -- -- data SupConfig i e o MkSupConfig :: SpawnFun i e o -> Timeout -> SupConfig i e o supConfigChildStopTimeout :: forall i_a16I9 e_a16Ia o_a16Ib. Lens' (SupConfig i_a16I9 e_a16Ia o_a16Ib) Timeout supConfigSpawnFun :: forall i_a16I9 e_a16Ia o_a16Ib i_a16Ja e_a16Jb o_a16Jc. Lens (SupConfig i_a16I9 e_a16Ia o_a16Ib) (SupConfig i_a16Ja e_a16Jb o_a16Jc) (SpawnFun i_a16I9 e_a16Ia o_a16Ib) (SpawnFun i_a16Ja e_a16Jb o_a16Jc) -- | Runtime-Errors occurring when spawning child-processes. data SpawnErr i o AlreadyStarted :: i -> o -> SpawnErr i o -- | Start and link a new supervisor process with the given -- SpawnFununction. -- -- To spawn new child processes use spawnChild. startSupervisor :: forall i e o. (HasCallStack, Member Logs e, Lifted IO e, Ord i, Tangible i, Tangible o) => SupConfig i (InterruptableProcess e) o -> Eff (InterruptableProcess e) (Sup i o) -- | Stop the supervisor and shutdown all processes. -- -- Block until the supervisor has finished. stopSupervisor :: (HasCallStack, Member Interrupts e, SetMember Process (Process q0) e, Member Logs e, Lifted IO e, Ord i, Tangible i, Tangible o) => Sup i o -> Eff e () -- | Check if a supervisor process is still alive. isSupervisorAlive :: (HasCallStack, Member Interrupts e, Member Logs e, Typeable i, Typeable o, NFData i, NFData o, Show i, Show o, SetMember Process (Process q0) e) => Sup i o -> Eff e Bool -- | Monitor a supervisor process. monitorSupervisor :: (HasCallStack, Member Interrupts e, Member Logs e, Typeable i, Typeable o, NFData i, NFData o, Show i, Show o, SetMember Process (Process q0) e) => Sup i o -> Eff e MonitorReference -- | Return a Text describing the current state of the supervisor. getDiagnosticInfo :: (Ord i, Tangible i, Tangible o, Typeable e, HasCallStack, Member Interrupts e, SetMember Process (Process q0) e) => Sup i o -> Eff e Text -- | Start, link and monitor a new child process using the SpawnFun -- passed to startSupervisor. spawnChild :: (HasCallStack, Member Interrupts e, Member Logs e, Ord i, Tangible i, Tangible o, SetMember Process (Process q0) e) => Sup i o -> i -> Eff e (Either (SpawnErr i o) o) -- | Lookup the given child-id and return the output value of the -- SpawnFun if the client process exists. lookupChild :: (HasCallStack, Member Interrupts e, Member Logs e, Ord i, Tangible i, Tangible o, SetMember Process (Process q0) e) => Sup i o -> i -> Eff e (Maybe o) -- | Stop a child process, and block until the child has exited. -- -- Return True if a process with that ID was found, False -- if no process with the given ID was running. stopChild :: (HasCallStack, Member Interrupts e, Member Logs e, Ord i, Tangible i, Tangible o, SetMember Process (Process q0) e) => Sup i o -> i -> Eff e Bool instance GHC.Generics.Generic (Control.Eff.Concurrent.Api.Supervisor.SpawnErr i o) instance (GHC.Show.Show i, GHC.Show.Show o) => GHC.Show.Show (Control.Eff.Concurrent.Api.Supervisor.SpawnErr i o) instance (GHC.Classes.Ord i, GHC.Classes.Ord o) => GHC.Classes.Ord (Control.Eff.Concurrent.Api.Supervisor.SpawnErr i o) instance (GHC.Classes.Eq i, GHC.Classes.Eq o) => GHC.Classes.Eq (Control.Eff.Concurrent.Api.Supervisor.SpawnErr i o) instance forall k1 (childId :: k1) k2 (spawnResult :: k2). Control.DeepSeq.NFData (Control.Eff.Concurrent.Api.Supervisor.Sup childId spawnResult) instance forall k1 (childId :: k1) k2 (spawnResult :: k2). GHC.Classes.Eq (Control.Eff.Concurrent.Api.Supervisor.Sup childId spawnResult) instance forall k1 (childId :: k1) k2 (spawnResult :: k2). GHC.Classes.Ord (Control.Eff.Concurrent.Api.Supervisor.Sup childId spawnResult) instance (Control.DeepSeq.NFData i, Control.DeepSeq.NFData o) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Api.Supervisor.SpawnErr i o) instance forall k1 k2 (childId :: k2) (spawnResult :: k1). (Data.Type.Pretty.PrettyTypeShow (Data.Type.Pretty.ToPretty childId), Data.Type.Pretty.PrettyTypeShow (Data.Type.Pretty.ToPretty spawnResult)) => GHC.Show.Show (Control.Eff.Concurrent.Api.Supervisor.Sup childId spawnResult) instance forall k i (o :: k) r. GHC.Show.Show i => GHC.Show.Show (Control.Eff.Concurrent.Api.Api (Control.Eff.Concurrent.Api.Supervisor.Sup i o) ('Control.Eff.Concurrent.Api.Synchronous r)) instance forall k i (o :: k) r. Control.DeepSeq.NFData i => Control.DeepSeq.NFData (Control.Eff.Concurrent.Api.Api (Control.Eff.Concurrent.Api.Supervisor.Sup i o) ('Control.Eff.Concurrent.Api.Synchronous r)) -- | Observer Effects -- -- This module supports the implementation of observers and observables. -- Expected use case is event propagation. module Control.Eff.Concurrent.Api.Observer -- | Describes a process that observes another via Asynchronous -- Api messages. -- -- An observer consists of a filter and a process id. The filter converts -- an observation to a message understood by the observer process, and -- the ProcessId is used to send the message. data Observer o [Observer] :: (PrettyTypeShow (ToPretty p), Show (Server p), Typeable p, Typeable o, NFData o, NFData (Api p 'Asynchronous)) => (o -> Maybe (Api p 'Asynchronous)) -> Server p -> Observer o -- | This data family defines an API, a communication interface description -- between at least two processes. The processes act as servers or -- client(s) regarding a specific instance of this type. -- -- The first parameter is usually a user defined phantom type that -- identifies the Api instance. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Api instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Also, for better logging, the an instance of ToPretty for the -- Api index type must be given. -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   data instance Api BookShop r where
--     RentBook  :: BookId   -> Api BookShop ('Synchronous (Either RentalError RentalId))
--     BringBack :: RentalId -> Api BookShop 'Asynchronous
--   
--   type instance ToPretty BookShop = PutStr "book shop"
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
data family Api (api :: Type) (reply :: Synchronicity) -- | And an Observer to the set of recipients for all observations -- reported by observed. Note that the observers are keyed by the -- observing process, i.e. a previous entry for the process contained in -- the Observer is overwritten. If you want multiple entries for a -- single process, just combine several filter functions. registerObserver :: (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o, NFData o, PrettyTypeShow (ToPretty o)) => Observer o -> Server (ObserverRegistry o) -> Eff r () -- | Send the ForgetObserver message forgetObserver :: (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o, NFData o, PrettyTypeShow (ToPretty o)) => Observer o -> Server (ObserverRegistry o) -> Eff r () -- | Based on the Api instance for Observer this simplified -- writing a callback handler for observations. In order to register to -- and ObserverRegistry use toObserver. handleObservations :: (HasCallStack, Typeable o, SetMember Process (Process q) r, NFData (Observer o)) => (o -> Eff r (CallbackResult 'Recoverable)) -> MessageCallback (Observer o) r -- | Use a Server as an Observer for -- handleObservations. toObserver :: (NFData o, Typeable o, NFData (Api (Observer o) 'Asynchronous), PrettyTypeShow (ToPretty o)) => Server (Observer o) -> Observer o -- | Create an Observer that conditionally accepts all observations -- of the given type and applies the given function to them; the function -- takes an observation and returns an Api cast that the observer -- server is compatible to. toObserverFor :: (Typeable a, PrettyTypeShow (ToPretty a), NFData (Api a 'Asynchronous), Typeable o, NFData o) => (o -> Api a 'Asynchronous) -> Server a -> Observer o -- | An Api for managing Observers, encompassing registration -- and de-registration of Observers. data ObserverRegistry o -- | Alias for the effect that contains the observers managed by -- manageObservers type ObserverState o = State (Observers o) -- | Provide the implementation for the ObserverRegistry Api, this -- handled RegisterObserver and ForgetObserver messages. It -- also adds the ObserverState constraint to the effect list. handleObserverRegistration :: forall o q r. (HasCallStack, Typeable o, SetMember Process (Process q) r, Member (ObserverState o) r, Member Logs r) => MessageCallback (ObserverRegistry o) r -- | Keep track of registered Observers. -- -- Handle the ObserverState introduced by -- handleObserverRegistration. manageObservers :: Eff (ObserverState o : r) a -> Eff r a -- | Report an observation to all observers. The process needs to -- manageObservers and to handleObserverRegistration. observed :: forall o r q. (SetMember Process (Process q) r, PrettyTypeShow (ToPretty o), Member (ObserverState o) r, Member Interrupts r) => o -> Eff r () instance forall k (o :: k) (r :: Control.Eff.Concurrent.Api.Synchronicity). Control.DeepSeq.NFData (Control.Eff.Concurrent.Api.Api (Control.Eff.Concurrent.Api.Observer.ObserverRegistry o) r) instance Control.DeepSeq.NFData o => Control.DeepSeq.NFData (Control.Eff.Concurrent.Api.Observer.Observer o) instance GHC.Show.Show (Control.Eff.Concurrent.Api.Observer.Observer o) instance GHC.Classes.Ord (Control.Eff.Concurrent.Api.Observer.Observer o) instance GHC.Classes.Eq (Control.Eff.Concurrent.Api.Observer.Observer o) instance Control.DeepSeq.NFData o => Control.DeepSeq.NFData (Control.Eff.Concurrent.Api.Api (Control.Eff.Concurrent.Api.Observer.Observer o) 'Control.Eff.Concurrent.Api.Asynchronous) -- | A small process to capture and _share_ observation's by enqueueing -- them into an STM TBQueue. module Control.Eff.Concurrent.Api.Observer.Queue -- | Contains a TBQueue capturing observations. See -- spawnLinkObservationQueueWriter, readObservationQueue. data ObservationQueue a -- | A Reader for an ObservationQueue. type ObservationQueueReader a = Reader (ObservationQueue a) -- | Read queued observations captured and enqueued in the shared -- TBQueue by spawnLinkObservationQueueWriter. This blocks -- until something was captured or an interrupt or exceptions was thrown. -- For a non-blocking variant use tryReadObservationQueue or -- flushObservationQueue. readObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r o -- | Read queued observations captured and enqueued in the shared -- TBQueue by spawnLinkObservationQueueWriter. Return the -- oldest enqueued observation immediately or Nothing if the queue -- is empty. Use readObservationQueue to block until an -- observation is observed. tryReadObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r (Maybe o) -- | Read at once all currently queued observations captured and enqueued -- in the shared TBQueue by -- spawnLinkObservationQueueWriter. This returns immediately all -- currently enqueued observations. For a blocking variant use -- readObservationQueue. flushObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r [o] -- | Create a mutable queue for observations. Use -- spawnLinkObservationQueueWriter for a simple way to get a -- process that enqueues all observations. -- --

Example

-- --
--   withObservationQueue 100 $ do
--     q  <- ask @(ObservationQueueReader TestEvent)
--     wq <- spawnLinkObservationQueueWriter q
--     registerObserver wq testServer
--     ...
--     cast testServer DoSomething
--     evt <- readObservationQueue @TestEvent
--     ...
--   
withObservationQueue :: forall o b e len. (HasCallStack, Typeable o, Show o, Member Logs e, Lifted IO e, Integral len, Member Interrupts e) => len -> Eff (ObservationQueueReader o : e) b -> Eff e b -- | Spawn a process that can be used as an Observer that enqueues -- the observations into an ObservationQueue. See -- withObservationQueue for an example. -- -- The observations can be obtained by readObservationQueue. All -- observations are captured up to the queue size limit, such that the -- first message received will be first message returned by -- readObservationQueue. spawnLinkObservationQueueWriter :: forall o q. (Tangible o, NFData (Api (Observer o) 'Asynchronous), Member Logs q, Lifted IO q, HasCallStack) => ObservationQueue o -> Eff (InterruptableProcess q) (Observer o) -- | Capture LogMessages to a Writer. -- -- See exampleLogCapture module Control.Eff.LogWriter.Capture -- | A LogWriter monad that provides pure logging by capturing via -- the Writer effect. -- -- See exampleLogCapture captureLogWriter :: LogWriter CaptureLogs -- | A LogWriter monad that provides pure logging by capturing via -- the Writer effect. newtype CaptureLogs a MkCaptureLogs :: Eff '[CaptureLogWriter] a -> CaptureLogs a [unCaptureLogs] :: CaptureLogs a -> Eff '[CaptureLogWriter] a -- | Alias for the Writer that contains the captured -- LogMessages from CaptureLogs. type CaptureLogWriter = Writer LogMessage -- | Run a Writer for LogMessages. -- -- Such a Writer is needed to handle CaptureLogWriter runCaptureLogWriter :: Eff (CaptureLogWriter : e) a -> Eff e (a, [LogMessage]) instance GHC.Base.Monad Control.Eff.LogWriter.Capture.CaptureLogs instance GHC.Base.Applicative Control.Eff.LogWriter.Capture.CaptureLogs instance GHC.Base.Functor Control.Eff.LogWriter.Capture.CaptureLogs instance Control.Eff.Log.Writer.HandleLogWriter Control.Eff.LogWriter.Capture.CaptureLogs -- | Functions for generic, IO-based LogWriters. -- -- This module is more low-level than the others in this directory. module Control.Eff.LogWriter.IO -- | A LogWriter that uses an IO action to write the message. -- -- This is just an alias for MkLogWriter but with IO as -- parameter. This reduces the need to apply something to the extra type -- argument @IO. -- -- Example use cases for this function are the consoleLogWriter -- and the ioHandleLogWriter. mkLogWriterIO :: HasCallStack => (LogMessage -> IO ()) -> LogWriter IO -- | A LogWriter that renders LogMessages to strings via -- renderLogMessageConsoleLog and prints them to an Handle -- using hPutStrLn. ioHandleLogWriter :: HasCallStack => Handle -> LogWriter IO -- | Decorate an IO based LogWriter to fill out these fields in -- LogMessages: -- -- -- -- It works by using mappingLogWriterM. defaultIoLogWriter :: Text -> Facility -> LogWriter IO -> LogWriter IO -- | Enable logging to IO using the defaultIoLogWriter. -- -- Example: -- --
--   exampleWithIoLogging :: IO ()
--   exampleWithIoLogging =
--       runLift
--     $ withIoLogging debugTraceLogWriter
--                     "my-app"
--                     local7
--                     (lmSeverityIsAtLeast informationalSeverity)
--     $ logInfo "Oh, hi there"
--   
withIoLogging :: SetMember Lift (Lift IO) e => LogWriter IO -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | The concrete list of Effects for logging with an IO based -- LogWriter, and a LogWriterReader. type LoggingAndIo = '[Logs, LogWriterReader IO, Lift IO] -- | Render a LogMessage but set the timestamp and thread id fields. printLogMessage :: LogMessage -> IO () -- | This helps to setup logging to a file. module Control.Eff.LogWriter.File -- | Enable logging to a file, with some LogMessage fields preset as -- described in withIoLogging. -- -- If the file or its directory does not exist, it will be created. -- -- Example: -- --
--   exampleWithFileLogging :: IO ()
--   exampleWithFileLogging =
--       runLift
--     $ withFileLogging "/var/log/my-app.log" "my-app" local7 allLogMessages
--     $ logInfo "Oh, hi there"
--   
-- -- To vary the LogWriter use withIoLogging. withFileLogging :: (Lifted IO e, MonadBaseControl IO (Eff e)) => FilePath -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | Enable logging to a file. -- -- If the file or its directory does not exist, it will be created. -- Example: -- --
--   exampleWithFileLogWriter :: IO ()
--   exampleWithFileLogWriter =
--       runLift
--     $ withSomeLogging @IO
--     $ withFileLogWriter "test.log"
--     $ logInfo "Oh, hi there"
--   
withFileLogWriter :: (Lifted IO e, LogsTo IO e, MonadBaseControl IO (Eff e)) => FilePath -> Eff e b -> Eff e b -- | This helps to setup logging via Debug.Trace. module Control.Eff.LogWriter.DebugTrace -- | Enable logging via traceM using the debugTraceLogWriter. -- The logging monad type can be any type with a Monad -- instance. -- -- Log messages are rendered using renderLogMessageConsoleLog. -- -- Example: -- --
--   exampleWithTraceLogWriter :: IO ()
--   exampleWithTraceLogWriter =
--       runLift
--     $ withSomeLogging @IO
--     $ withTraceLogWriter
--     $ logInfo "Oh, hi there"
--   
withTraceLogWriter :: forall h e a. (Monad h, LogsTo h e) => Eff e a -> Eff e a -- | Enable logging via traceM using the debugTraceLogWriter, -- with some LogMessage fields preset as in withIoLogging. -- -- Log messages are rendered using renderLogMessageConsoleLog. -- -- Example: -- --
--   exampleWithTraceLogging :: IO ()
--   exampleWithTraceLogging =
--       runLift
--     $ withTraceLogging "my-app" local7 allLogMessages
--     $ logInfo "Oh, hi there"
--   
withTraceLogging :: Lifted IO e => Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | Write LogMessages via traceM. debugTraceLogWriter :: forall h. Monad h => LogMessageRenderer Text -> LogWriter h -- | This helps to setup logging to standard ouput. module Control.Eff.LogWriter.Console -- | Enable logging to standard output using the -- consoleLogWriter. -- -- Log messages are rendered using renderLogMessageConsoleLog. -- -- Example: -- --
--   exampleWithConsoleLogWriter :: IO ()
--   exampleWithConsoleLogWriter =
--       runLift
--     $ withSomeLogging @IO
--     $ withConsoleLogWriter
--     $ logInfo "Oh, hi there"
--   
withConsoleLogWriter :: (LogsTo IO e, Lifted IO e) => Eff e a -> Eff e a -- | Enable logging to standard output using the -- consoleLogWriter, with some LogMessage fields preset as -- in withIoLogging. -- -- Log messages are rendered using renderLogMessageConsoleLog. -- -- Example: -- --
--   exampleWithConsoleLogging :: IO ()
--   exampleWithConsoleLogging =
--       runLift
--     $ withConsoleLogging "my-app" local7 allLogMessages
--     $ logInfo "Oh, hi there"
--   
-- -- To vary the LogWriter use withIoLogging. withConsoleLogging :: Lifted IO e => Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | Write LogMessages to standard output, formatted with -- printLogMessage. -- -- It uses stdoutLogWriter with renderLogMessageConsoleLog. consoleLogWriter :: LogWriter IO -- | A LogWriter that uses a LogMessageRenderer to render, -- and putStrLn to print it. stdoutLogWriter :: LogMessageRenderer Text -> LogWriter IO -- | This module only exposes a LogWriter for asynchronous logging; module Control.Eff.LogWriter.Async -- | Move the current LogWriter into its own thread. -- -- A bounded queue is used to forward logs to the process. -- -- If an exception is received, the logging process will be killed. -- -- Log messages are deeply evaluated before being sent to the logger -- process, to prevent that lazy evaluation leads to heavy work being -- done in the logger process instead of the caller process. -- -- Example: -- --
--   exampleAsyncLogWriter :: IO ()
--   exampleAsyncLogWriter =
--       runLift
--     $ withLogging consoleLogWriter
--     $ withAsyncLogWriter (1000::Int)
--     $ do logMsg "test 1"
--          logMsg "test 2"
--          logMsg "test 3"
--   
withAsyncLogWriter :: (LogsTo IO e, Lifted IO e, MonadBaseControl IO (Eff e), Integral len) => len -> Eff e a -> Eff e a -- | This is a wrapper around withAsyncLogWriter and -- withIoLogging. -- -- Example: -- --
--   exampleWithAsyncLogging :: IO ()
--   exampleWithAsyncLogging =
--       runLift
--     $ withAsyncLogWriter consoleLogWriter (1000::Int) "my-app" local0 allLogMessages
--     $ do logMsg "test 1"
--          logMsg "test 2"
--          logMsg "test 3"
--   
withAsyncLogging :: (Lifted IO e, MonadBaseControl IO (Eff e), Integral len) => LogWriter IO -> len -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | A coroutine based, single threaded scheduler for Processes. module Control.Eff.Concurrent.Process.SingleThreadedScheduler -- | Handle the Process effect, as well as all lower effects using -- an effect handler function. -- -- Execute the main Process and all the other processes -- spawned by it in the current thread concurrently, using a -- co-routine based, round-robin scheduler. If a process exits with eg.g -- exitNormally or exitWithError or is killed by another -- process Left ... is returned. Otherwise, the result will be -- wrapped in a Right. -- -- Every time a process _yields_ the effects are evaluated down to the a -- value of type m (Either String a). -- -- If the evaluator function runs the action down e.g. IO this -- might improve memory consumption, for long running services, with -- processes that loop endlessly. scheduleM :: forall m r a. Monad m => (forall b. Eff r b -> m b) -> m () -> Eff (InterruptableProcess r) a -> m (Either (Interrupt 'NoRecovery) a) -- | Like scheduleIO but pure. The yield effect is -- just return (). schedulePure == runIdentity . -- scheduleM (Identity . run) (return ()) schedulePure :: Eff (InterruptableProcess '[Logs, LogWriterReader PureLogWriter]) a -> Either (Interrupt 'NoRecovery) a -- | Invoke scheduleM with lift yield as yield -- effect. scheduleIO runEff == scheduleM (runLift . runEff) -- (liftIO yield) scheduleIO :: MonadIO m => (forall b. Eff r b -> Eff '[Lift m] b) -> Eff (InterruptableProcess r) a -> m (Either (Interrupt 'NoRecovery) a) -- | Invoke scheduleM with lift yield as yield -- effect. scheduleMonadIOEff == scheduleM id (liftIO -- yield) scheduleMonadIOEff :: MonadIO (Eff r) => Eff (InterruptableProcess r) a -> Eff r (Either (Interrupt 'NoRecovery) a) -- | Run processes that have the Logs and the Lift effects. -- The user must provide a log handler function. -- -- Log messages are evaluated strict. -- --
--   scheduleIOWithLogging == scheduleIO . withLogging
--   
scheduleIOWithLogging :: HasCallStack => LogWriter IO -> Eff (InterruptableProcess LoggingAndIo) a -> IO (Either (Interrupt 'NoRecovery) a) -- | Execute a Process using scheduleM on top of Lift -- IO and withLogging String effects. defaultMainSingleThreaded :: HasCallStack => Eff (InterruptableProcess LoggingAndIo) () -> IO () instance GHC.Show.Show (Control.Eff.Concurrent.Process.SingleThreadedScheduler.OnYield r a) instance GHC.Show.Show (Control.Eff.Concurrent.Process.SingleThreadedScheduler.STS r m) -- | Implement Erlang style message passing concurrency. -- -- This module contains spawn which handles the Process -- effects, using TQueues and withAsync. -- -- This aims to be a pragmatic implementation, so even logging is -- supported. -- -- At the core is a main process that enters schedule and -- creates all of the internal state stored in TVars to manage -- processes with message queues. module Control.Eff.Concurrent.Process.ForkIOScheduler -- | This is the main entry point to running a message passing concurrency -- application. This function takes a Process on top of the -- SchedulerIO effect for concurrent logging. schedule :: HasCallStack => Eff InterruptableProcEff () -> Eff LoggingAndIo () -- | Start the message passing concurrency system then execute a -- Process on top of SchedulerIO effect. All logging is -- sent to standard output. defaultMain :: HasCallStack => Eff InterruptableProcEff () -> IO () -- | Start the message passing concurrency system then execute a -- Process on top of SchedulerIO effect. All logging is -- sent to standard output. defaultMainWithLogWriter :: HasCallStack => LogWriter IO -> Eff InterruptableProcEff () -> IO () -- | The concrete list of Effects of processes compatible with this -- scheduler. This builds upon SchedulerIO. type ProcEff = ConsProcess SchedulerIO -- | The concrete list of the effects, that the Process uses type InterruptableProcEff = InterruptableProcess SchedulerIO -- | The concrete list of Effects for this scheduler implementation. type SchedulerIO = (Reader SchedulerState : LoggingAndIo) -- | Type class constraint to indicate that an effect union contains the -- effects required by every process and the scheduler implementation -- itself. type HasSchedulerIO r = (HasCallStack, Lifted IO r, SchedulerIO <:: r) instance GHC.Show.Show Control.Eff.Concurrent.Process.ForkIOScheduler.ProcessInfo instance Data.Default.Class.Default Control.Eff.Concurrent.Process.ForkIOScheduler.MessageQ -- | This helps to setup logging to standard ouput. module Control.Eff.LogWriter.UDP -- | Enable logging to a (remote-) host via UDP. -- -- See exampleUdpRFC3164Logging withUDPLogWriter :: (Lifted IO e, LogsTo IO e, MonadBaseControl IO (Eff e), HasCallStack) => (LogMessage -> Text) -> String -> String -> Eff e b -> Eff e b -- | Enable logging to a remote host via UDP, with some -- LogMessage fields preset as in withIoLogging. -- -- See exampleUdpRFC3164Logging withUDPLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => (LogMessage -> Text) -> String -> String -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | This helps to setup logging to standard ouput. module Control.Eff.LogWriter.UnixSocket -- | Enable logging to a (remote-) host via UnixSocket. -- -- See exampleDevLogSyslogLogging withUnixSocketLogWriter :: (Lifted IO e, LogsTo IO e, MonadBaseControl IO (Eff e), HasCallStack) => LogMessageRenderer Text -> FilePath -> Eff e b -> Eff e b -- | Enable logging to a unix domain socket, with some -- LogMessage fields preset as in withIoLogging. -- -- See exampleDevLogSyslogLogging withUnixSocketLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => LogMessageRenderer Text -> FilePath -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | Examples for Logging. module Control.Eff.Log.Examples -- | Example code for: -- -- exampleLogging :: HasCallStack => IO () -- | Example code for: -- -- exampleWithLogging :: HasCallStack => IO () -- | Example code for: -- -- exampleWithSomeLogging :: HasCallStack => () -- | Example code for: -- -- exampleLogPredicate :: HasCallStack => IO Int -- | Example code for: -- -- exampleLogCapture :: IO () -- | Example code for: -- -- exampleAsyncLogging :: IO () -- | Example code for RFC5424 formatted logs. exampleRFC5424Logging :: IO Int -- | Example code for RFC3164 with RFC5424 time stamp formatted logs. exampleRFC3164WithRFC5424TimestampsLogging :: IO Int -- | Example code logging via a unix domain socket to -- devlog. exampleDevLogSyslogLogging :: IO Int -- | Example code logging via a unix domain socket to -- devlog. exampleDevLogRFC5424Logging :: IO Int -- | Example code logging RFC5424 via UDP port 514 on localhost. exampleUdpRFC5424Logging :: IO Int -- | Example code logging RFC5424 via UDP port 514 on localhost. exampleUdpRFC3164Logging :: IO Int -- | Example logging client code -- -- loggingExampleClient :: (HasCallStack, Monad h, LogsTo h e) => Eff e () -- | Example logging client code using many LogPredicates. -- -- logPredicatesExampleClient :: (HasCallStack, Monad h, LogsTo h e) => Eff e Int -- | In rare occasions GHC optimizes innocent looking loops into -- space-leaking monsters. See the discussion here: GHC issue -- 13080 for more details, or this blog post about space leaks in -- nested loops -- -- These functions in this module might help, at least in -- conjunction with the -fno-full-laziness GHC option. -- -- There is a unit test in the sources of this module, which can be used -- to do a comparative heap profiling of these function vs. their -- counterparts in the base package. -- -- Here are the images of the profiling results, the images show that the -- functions in this module do not leak space, compared to the original -- functions (forever and replicateM_): -- module Control.Eff.Loop -- | A version of forever that hopefully tricks GHC into -- not creating a space leak. The intuition is, that we -- want to do something that is cheap, and hence should be -- recomputed instead of shared. foreverCheap :: Monad m => m a -> m () -- | A version of replicateM_ that hopefully tricks GHC into -- not creating a space leak. The intuition is, that we -- want to do something that is cheap, and hence should be -- recomputed instead of shared. replicateCheapM_ :: Monad m => Int -> m a -> m () -- | Erlang style processes with message passing concurrency based on -- (more) extensible-effects. module Control.Eff.Concurrent -- | Each process is identified by a single process id, that stays constant -- throughout the life cycle of a process. Also, message sending relies -- on these values to address messages to processes. newtype ProcessId ProcessId :: Int -> ProcessId [_fromProcessId] :: ProcessId -> Int -- | A monitored process exited. This message is sent to a process by the -- scheduler, when a process that was monitored died. data ProcessDown ProcessDown :: !MonitorReference -> !SomeExitReason -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !SomeExitReason -- | A value that contains a unique reference of a process monitoring. data MonitorReference MonitorReference :: Int -> ProcessId -> MonitorReference [monitorIndex] :: MonitorReference -> Int [monitoredProcess] :: MonitorReference -> ProcessId -- | An existential wrapper around Interrupt data SomeExitReason [SomeExitReason] :: Interrupt x -> SomeExitReason -- | This adds a layer of the Interrupts effect on top of -- ConsProcess type InterruptableProcess e = Interrupts : ConsProcess e -- | Exceptions containing Interrupts. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc (Interrupt 'Recoverable) -- | Interrupts which are Recoverable. type RecoverableInterrupt = Interrupt 'Recoverable -- | A sum-type with reasons for why a process operation, such as receiving -- messages, is interrupted in the scheduling loop. -- -- This includes errors, that can occur when scheduling messages. data Interrupt (t :: ExitRecovery) -- | A process has finished a unit of work and might exit or work on -- something else. This is primarily used for interrupting infinite -- server loops, allowing for additional cleanup work before exiting -- (e.g. with ExitNormally) [NormalExitRequested] :: Interrupt 'Recoverable -- | A process that should be running was not running. [OtherProcessNotRunning] :: ProcessId -> Interrupt 'Recoverable -- | A Recoverable timeout has occurred. [TimeoutInterrupt] :: String -> Interrupt 'Recoverable -- | A linked process is down [LinkedProcessCrashed] :: ProcessId -> Interrupt 'Recoverable -- | An exit reason that has an error message and is Recoverable. [ErrorInterrupt] :: String -> Interrupt 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: Interrupt 'NoRecovery -- | An error causes the process to exit immediately. For example an -- unexpected runtime exception was thrown, i.e. an exception derived -- from SomeException Or a Recoverable Interrupt was not -- recoverd. [ExitUnhandledError] :: Text -> Interrupt 'NoRecovery -- | A process shall exit immediately, without any cleanup was cancelled -- (e.g. killed, in cancel) [ExitProcessCancelled] :: Interrupt 'NoRecovery -- | This value indicates whether a process exited in way consistent with -- the planned behaviour or not. data ExitSeverity NormalExit :: ExitSeverity Crash :: ExitSeverity -- | This kind is used to indicate if a Interrupt can be treated -- like a short interrupt which can be handled or ignored. data ExitRecovery Recoverable :: ExitRecovery NoRecovery :: ExitRecovery -- | The state that a Process is currently in. data ProcessState -- | The process has just been started but not scheduled yet. ProcessBooting :: ProcessState -- | The process yielded it's time slice ProcessIdle :: ProcessState -- | The process is busy with non-blocking ProcessBusy :: ProcessState -- | The process is busy with sending a message ProcessBusySending :: ProcessState -- | The process is busy with killing ProcessBusySendingShutdown :: ProcessState -- | The process is busy with killing ProcessBusySendingInterrupt :: ProcessState -- | The process blocked by a receiveAnyMessage ProcessBusyReceiving :: ProcessState -- | The process blocked by a linkProcess ProcessBusyLinking :: ProcessState -- | The process blocked by a unlinkProcess ProcessBusyUnlinking :: ProcessState -- | The process blocked by a monitor ProcessBusyMonitoring :: ProcessState -- | The process blocked by a demonitor ProcessBusyDemonitoring :: ProcessState -- | The process was interrupted ProcessInterrupted :: ProcessState -- | The process was shutdown or crashed ProcessShuttingDown :: ProcessState -- | Cons Process onto a list of effects. type ConsProcess r = Process r : r -- | A function that decided if the next message will be received by -- ReceiveSelectedMessage. It conveniently is an instance of -- Alternative so the message selector can be combined: > > -- selectInt :: MessageSelector Int > selectInt = selectMessage > -- > selectString :: MessageSelector String > selectString = -- selectMessage > > selectIntOrString :: MessageSelector (Either -- Int String) > selectIntOrString = > Left $ -- selectTimeout| Right $ selectString data MessageSelector a -- | Every Process action returns it's actual result wrapped in this -- type. It will allow to signal errors as well as pass on normal results -- such as incoming messages. data ResumeProcess v -- | The current operation of the process was interrupted with a -- Interrupt. If isRecoverable holds for the given reason, -- the process may choose to continue. [Interrupted] :: Interrupt 'Recoverable -> ResumeProcess v -- | The process may resume to do work, using the given result. [ResumeWith] :: a -> ResumeProcess a -- | Data flows between Processes via these messages. -- -- This is just a newtype wrapper around Dynamic. The reason this -- type exists is to force construction through the code in this module, -- which always evaluates a message to normal form before -- sending it to another process. data StrictDynamic -- | The process effect is the basis for message passing concurrency. This -- effect describes an interface for concurrent, communicating isolated -- processes identified uniquely by a process-id. -- -- Processes can raise exceptions that can be caught, exit gracefully or -- with an error, or be killed by other processes, with the option of -- ignoring the shutdown request. -- -- Process Scheduling is implemented in different modules. All scheduler -- implementations should follow some basic rules: -- -- data Process (r :: [Type -> Type]) b -- | Remove all messages from the process' message queue [FlushMessages] :: Process r (ResumeProcess [StrictDynamic]) -- | In cooperative schedulers, this will give processing time to the -- scheduler. Every other operation implicitly serves the same purpose. [YieldProcess] :: Process r (ResumeProcess ()) -- | Return the current ProcessId [SelfPid] :: Process r (ResumeProcess ProcessId) -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. [Spawn] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Get the process state (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe ProcessState)) -- | Shutdown the process; irregardless of the exit reason, this function -- never returns, [Shutdown] :: Interrupt 'NoRecovery -> Process r a -- | Shutdown another process immediately, the other process has no way of -- handling this! [SendShutdown] :: ProcessId -> Interrupt 'NoRecovery -> Process r (ResumeProcess ()) -- | Request that another a process interrupts. The targeted process is -- interrupted and gets an Interrupted, the target process may -- decide to ignore the interrupt and continue as if nothing happened. [SendInterrupt] :: ProcessId -> Interrupt 'Recoverable -> Process r (ResumeProcess ()) -- | Send a message to a process addressed by the ProcessId. Sending -- a message should always succeed and return immediately, -- even if the destination process does not exist, or does not accept -- messages of the given type. [SendMessage] :: ProcessId -> StrictDynamic -> Process r (ResumeProcess ()) -- | Receive a message that matches a criteria. This should block until an -- a message was received. The message is returned as a -- ResumeProcess value. The function should also return if an -- exception was caught or a shutdown was requested. [ReceiveSelectedMessage] :: forall r a. MessageSelector a -> Process r (ResumeProcess a) -- | Generate a unique Int for the current process. [MakeReference] :: Process r (ResumeProcess Int) -- | Monitor another process. When the monitored process exits a -- ProcessDown is sent to the calling process. The return value is -- a unique identifier for that monitor. There can be multiple monitors -- on the same process, and a message for each will be sent. If the -- process is already dead, the ProcessDown message will be sent -- immediately, without exit reason [Monitor] :: ProcessId -> Process r (ResumeProcess MonitorReference) -- | Remove a monitor. [Demonitor] :: MonitorReference -> Process r (ResumeProcess ()) -- | Connect the calling process to another process, such that if one of -- the processes crashes (i.e. isCrash returns True), the -- other is shutdown with the Interrupt -- LinkedProcessCrashed. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Deeply evaluate the given value and wrap it into a -- StrictDynamic. toStrictDynamic :: (Typeable a, NFData a) => a -> StrictDynamic -- | Convert a StrictDynamic back to a value. fromStrictDynamic :: Typeable a => StrictDynamic -> Maybe a -- | Convert a StrictDynamic back to an unwrapped Dynamic. unwrapStrictDynamic :: StrictDynamic -> Dynamic -- | Create a message selector for a value that can be obtained by -- fromStrictDynamic. selectMessage :: Typeable t => MessageSelector t -- | Create a message selector from a predicate. filterMessage :: Typeable a => (a -> Bool) -> MessageSelector a -- | Select a message of type a and apply the given function to -- it. If the function returns Just The -- ReceiveSelectedMessage function will return the result (sans -- Maybe). selectMessageWith :: Typeable a => (a -> Maybe b) -> MessageSelector b -- | Create a message selector. selectDynamicMessage :: (StrictDynamic -> Maybe a) -> MessageSelector a -- | Create a message selector that will match every message. This is -- lazy because the result is not forceed. selectAnyMessage :: MessageSelector StrictDynamic -- | Get the ExitRecovery toExitRecovery :: Interrupt r -> ExitRecovery -- | Get the ExitSeverity of a Interrupt. toExitSeverity :: Interrupt e -> ExitSeverity -- | Return either ExitNormally or interruptToExit from a -- Recoverable Interrupt; -- -- If the Interrupt is NormalExitRequested then return -- ExitNormally interruptToExit :: Interrupt 'Recoverable -> Interrupt 'NoRecovery -- | A predicate for linked process crashes. isProcessDownInterrupt :: Maybe ProcessId -> Interrupt r -> Bool -- | Handle all Interrupts of an InterruptableProcess by -- wrapping them up in interruptToExit and then do a process -- Shutdown. provideInterruptsShutdown :: forall e a. Eff (InterruptableProcess e) a -> Eff (ConsProcess e) a -- | Handle Interrupts arising during process operations, e.g. when -- a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. handleInterrupts :: (HasCallStack, Member Interrupts r) => (Interrupt 'Recoverable -> Eff r a) -> Eff r a -> Eff r a -- | Like handleInterrupts, but instead of passing the -- Interrupt to a handler function, Either is returned. tryUninterrupted :: (HasCallStack, Member Interrupts r) => Eff r a -> Eff r (Either (Interrupt 'Recoverable) a) -- | Handle interrupts by logging them with logProcessExit and -- otherwise ignoring them. logInterrupts :: forall r. (Member Logs r, HasCallStack, Member Interrupts r) => Eff r () -> Eff r () -- | Handle Interrupts arising during process operations, e.g. when -- a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. exitOnInterrupt :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r) => Eff r a -> Eff r a -- | Handle Interrupts arising during process operations, e.g. when -- a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. provideInterrupts :: HasCallStack => Eff (Interrupts : r) a -> Eff r (Either (Interrupt 'Recoverable) a) -- | Wrap all (left) Interrupts into interruptToExit and -- return the (right) NoRecovery Interrupts as is. mergeEitherInterruptAndExitReason :: Either (Interrupt 'Recoverable) (Interrupt 'NoRecovery) -> Interrupt 'NoRecovery -- | Throw an Interrupt, can be handled by handleInterrupts -- or exitOnInterrupt or provideInterrupts. interrupt :: (HasCallStack, Member Interrupts r) => Interrupt 'Recoverable -> Eff r a -- | A predicate for crashes. A crash happens when a process exits -- with an Interrupt other than ExitNormally isCrash :: Interrupt x -> Bool -- | A predicate for recoverable exit reasons. This predicate defines the -- exit reasons which functions such as executeAndResume isRecoverable :: Interrupt x -> Bool -- | Partition a SomeExitReason back into either a NoRecovery -- or a Recoverable Interrupt fromSomeExitReason :: SomeExitReason -> Either (Interrupt 'NoRecovery) (Interrupt 'Recoverable) -- | Print a Interrupt to Just a formatted String when -- isCrash is True. This can be useful in combination with -- view patterns, e.g.: -- --
--   logCrash :: Interrupt -> Eff e ()
--   logCrash (toCrashReason -> Just reason) = logError reason
--   logCrash _ = return ()
--   
-- -- Though this can be improved to: -- --
--   logCrash = traverse_ logError . toCrashReason
--   
toCrashReason :: Interrupt x -> Maybe Text -- | Log the Interrupts logProcessExit :: forall e x. (Member Logs e, HasCallStack) => Interrupt x -> Eff e () -- | Execute a and action and return the result; if the process is -- interrupted by an error or exception, or an explicit shutdown from -- another process, or through a crash of a linked process, i.e. whenever -- the exit reason satisfies isRecoverable, return the exit -- reason. executeAndResume :: forall q r v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r (Either (Interrupt 'Recoverable) v) -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrExit :: forall r q v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrThrow :: forall q r v. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Process q (ResumeProcess v) -> Eff r v -- | Use executeAndResumeOrExit to execute YieldProcess. -- Refer to YieldProcess for more information. yieldProcess :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Eff r () -- | Send a message to a process addressed by the ProcessId. See -- SendMessage. -- -- The message will be reduced to normal form (rnf) by/in the -- caller process. sendMessage :: forall r q o. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o, NFData o) => ProcessId -> o -> Eff r () -- | Send a Dynamic value to a process addressed by the -- ProcessId. See SendMessage. sendAnyMessage :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> StrictDynamic -> Eff r () -- | Exit a process addressed by the ProcessId. The process will -- exit, it might do some cleanup, but is ultimately unrecoverable. See -- SendShutdown. sendShutdown :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> Interrupt 'NoRecovery -> Eff r () -- | Interrupts a process addressed by the ProcessId. The process -- might exit, or it may continue. | Like sendInterrupt, but also -- return True iff the process to exit exists. sendInterrupt :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> Interrupt 'Recoverable -> Eff r () -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. If the new -- process is interrupted, the process will Shutdown with the -- Interrupt wrapped in interruptToExit. For specific use -- cases it might be better to use spawnRaw. spawn :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r () -- | Start a new process, and immediately link to it. spawnLink :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. The spawned -- process has only the raw ConsProcess effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r () -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r Bool -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r StrictDynamic -- | Block until a message was received, that is not Nothing after -- applying a callback to it. See ReceiveSelectedMessage for more -- documentation. receiveSelectedMessage :: forall r q a. (HasCallStack, Show a, SetMember Process (Process q) r, Member Interrupts r) => MessageSelector a -> Eff r a -- | Receive and cast the message to some Typeable instance. See -- ReceiveSelectedMessage for more documentation. This will wait -- for a message of the return type using receiveSelectedMessage receiveMessage :: forall a r q. (HasCallStack, Typeable a, NFData a, Show a, SetMember Process (Process q) r, Member Interrupts r) => Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r [StrictDynamic] -- | Enter a loop to receive messages and pass them to a callback, until -- the function returns Just a result. Only the messages of the -- given type will be received. If the process is interrupted by an -- exception of by a SendShutdown from another process, with an -- exit reason that satisfies isRecoverable, then the callback -- will be invoked with Left Interrupt, otherwise -- the process will be exited with the same reason using -- exitBecause. See also ReceiveSelectedMessage for more -- documentation. receiveSelectedLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => MessageSelector a -> (Either (Interrupt 'Recoverable) a -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Like receiveSelectedLoop but not selective. See also -- selectAnyMessage, receiveSelectedLoop. receiveAnyLoop :: forall r q endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => (Either (Interrupt 'Recoverable) StrictDynamic -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Like receiveSelectedLoop but refined to casting to a specific -- Typeable using selectMessage. receiveLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack, NFData a, Typeable a) => (Either (Interrupt 'Recoverable) a -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Returns the ProcessId of the current process. self :: (HasCallStack, SetMember Process (Process q) r) => Eff r ProcessId -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r Int -- | Monitor another process. When the monitored process exits a -- ProcessDown is sent to the calling process. The return value is -- a unique identifier for that monitor. There can be multiple monitors -- on the same process, and a message for each will be sent. If the -- process is already dead, the ProcessDown message will be sent -- immediately, without exit reason monitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => MonitorReference -> Eff r () -- | monitor another process before while performing an action and -- demonitor afterwards. withMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r, Typeable a, Show a) => ProcessId -> MessageSelector a -> Eff r (Either ProcessDown a) -- | Make an Interrupt for a ProcessDown message. -- -- For example: doSomething >>= either (interrupt . -- becauseProcessIsDown) return becauseProcessIsDown :: ProcessDown -> Interrupt 'Recoverable -- | A MessageSelector for the ProcessDown message of a -- specific process. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | Connect the calling process to another process, such that if one of -- the processes crashes (i.e. isCrash returns True), the -- other is shutdown with the Interrupt -- LinkedProcessCrashed. linkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. unlinkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Exit the process with a Interrupt. exitBecause :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => Interrupt 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => String -> Eff r a fromProcessId :: Iso' ProcessId Int -- | A value to be sent when timer started with startTimer has -- elapsed. data TimerElapsed -- | The reference to a timer started by startTimer, required to -- stop a timer via cancelTimer. data TimerReference -- | A number of micro seconds. data Timeout -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfter. receiveAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable a, NFData a, Show a) => Timeout -> Eff r (Maybe a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Left TimerElapsed. This -- is based on selectTimerElapsed and startTimer. receiveSelectedAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Show a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitor combined with -- receiveSelectedAfter. receiveSelectedWithMonitorAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Show a) => ProcessId -> MessageSelector a -> Timeout -> Eff r (Either (Either ProcessDown TimerElapsed) a) -- | A MessageSelector matching TimerElapsed messages created -- by startTimer. selectTimerElapsed :: TimerReference -> MessageSelector TimerElapsed -- | Send a message to a given process after waiting. The message is -- created by applying the function parameter to the -- TimerReference, such that the message can directly refer to the -- timer. sendAfter :: forall r q message. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable message, NFData message) => ProcessId -> Timeout -> (TimerReference -> message) -> Eff r TimerReference -- | Start a new timer, after the time has elapsed, TimerElapsed is -- sent to calling process. The message also contains the -- TimerReference returned by this function. Use -- cancelTimer to cancel the timer. Use selectTimerElapsed -- to receive the message using receiveSelectedMessage. To receive -- messages with guarded with a timeout see receiveAfter. startTimer :: forall r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Timeout -> Eff r TimerReference -- | Cancel a timer started with startTimer. cancelTimer :: forall r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => TimerReference -> Eff r () -- | This is a tag-type that wraps around a ProcessId and holds an -- Api index type. newtype Server api Server :: ProcessId -> Server api [_fromServer] :: Server api -> ProcessId -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Api -- instance. data Synchronicity -- | Specify that handling a request is a blocking operation with a -- specific return type, e.g. ('Synchronous (Either RentalError -- RentalId)) Synchronous :: Type -> Synchronicity -- | Non-blocking, asynchronous, request handling Asynchronous :: Synchronicity -- | A set of constraints for types that can evaluated via NFData, -- compared via Ord and presented dynamically via Typeable, -- and represented both as values via Show, as well as on the type -- level via ToPretty. type Tangible i = (NFData i, Typeable i, Show i, PrettyTypeShow (ToPretty i)) -- | This data family defines an API, a communication interface description -- between at least two processes. The processes act as servers or -- client(s) regarding a specific instance of this type. -- -- The first parameter is usually a user defined phantom type that -- identifies the Api instance. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Api instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Also, for better logging, the an instance of ToPretty for the -- Api index type must be given. -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   data instance Api BookShop r where
--     RentBook  :: BookId   -> Api BookShop ('Synchronous (Either RentalError RentalId))
--     BringBack :: RentalId -> Api BookShop 'Asynchronous
--   
--   type instance ToPretty BookShop = PutStr "book shop"
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
data family Api (api :: Type) (reply :: Synchronicity) fromServer :: forall api_aXxm api_aXVF. Iso (Server api_aXxm) (Server api_aXVF) ProcessId ProcessId -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API proxyAsServer :: proxy api -> ProcessId -> Server api -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API asServer :: forall api. ProcessId -> Server api -- | The reader effect for ProcessIds for Apis, see -- registerServer type ServerReader o = Reader (Server o) -- | Instead of passing around a Server value and passing to -- functions like cast or call, a Server can -- provided by a Reader effect, if there is only a single -- server for a given Api instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Server. type ServesApi o r q = (Typeable o, PrettyTypeShow (ToPretty o), SetMember Process (Process q) r, Member (ServerReader o) r) -- | Send an Api request that has no return value and return as fast -- as possible. The type signature enforces that the corresponding -- Api clause is Asynchronous. The operation never fails, -- if it is important to know if the message was delivered, use -- call instead. -- -- The message will be reduced to normal form (rnf) in the caller -- process. cast :: forall r q o. (HasCallStack, SetMember Process (Process q) r, PrettyTypeShow (ToPretty o), Member Interrupts r, Typeable o, Typeable (Api o 'Asynchronous), NFData (Api o 'Asynchronous)) => Server o -> Api o 'Asynchronous -> Eff r () -- | Send an Api request and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Api clause -- is Synchronous. -- -- Always prefer callWithTimeout over call call :: forall result api r q. (SetMember Process (Process q) r, Member Interrupts r, Typeable api, PrettyTypeShow (ToPretty api), Typeable (Api api ( 'Synchronous result)), NFData (Api api ( 'Synchronous result)), Typeable result, NFData result, Show result, HasCallStack) => Server api -> Api api ( 'Synchronous result) -> Eff r result -- | Send an Api request and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Api clause -- is Synchronous. -- -- If the server that was called dies, this function interrupts the -- process with ProcessDown. If the server takes longer to reply -- than the given timeout, this function interrupts the process with -- TimeoutInterrupt. -- -- Always prefer this function over call callWithTimeout :: forall result api r q. (SetMember Process (Process q) r, Member Interrupts r, Typeable api, Typeable (Api api ( 'Synchronous result)), NFData (Api api ( 'Synchronous result)), Typeable result, NFData result, Show result, Member Logs r, Lifted IO q, Lifted IO r, HasCallStack, PrettyTypeShow (ToPretty api)) => Server api -> Api api ( 'Synchronous result) -> Timeout -> Eff r result -- | Run a reader effect that contains the one server handling a -- specific Api instance. registerServer :: HasCallStack => Server o -> Eff (ServerReader o : r) a -> Eff r a -- | Get the Server registered with registerServer. whereIsServer :: Member (ServerReader o) e => Eff e (Server o) -- | Like call but take the Server from the reader provided -- by registerServer. callRegistered :: (Typeable reply, ServesApi o r q, HasCallStack, NFData reply, Show reply, NFData (Api o ( 'Synchronous reply)), Member Interrupts r) => Api o ( 'Synchronous reply) -> Eff r reply -- | Like cast but take the Server from the reader provided -- by registerServer. castRegistered :: (Typeable o, ServesApi o r q, HasCallStack, Member Interrupts r, NFData (Api o 'Asynchronous)) => Api o 'Asynchronous -> Eff r () -- | Just a wrapper around a function that will be applied to the result of -- a MessageCallbacks StopServer clause, or an -- Interrupt caught during the execution of receive or a -- MessageCallback data InterruptCallback eff [InterruptCallback] :: (Interrupt 'Recoverable -> Eff eff (CallbackResult 'NoRecovery)) -> InterruptCallback eff -- | Helper type class for the return values of spawnApiServer et -- al. class ToServerPids (t :: k) where { type family PrettyServerPids t :: PrettyType; type family ServerPids t; } toServerPids :: ToServerPids t => proxy t -> ProcessId -> ServerPids t -- | An existential wrapper around a MessageSelector and a function -- that handles the selected message. The api type parameter is -- a phantom type. -- -- The return value of the handler function is a CallbackResult. data MessageCallback api eff [MessageCallback] :: MessageSelector a -> (a -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback api eff -- | A command to the server loop started by apiServerLoop. -- Typically returned by a MessageCallback to indicate if the -- server should continue or stop. data CallbackResult (r :: ExitRecovery) -- | Tell the server to keep the server loop running [AwaitNext] :: CallbackResult r -- | Tell the server to exit, this will cause apiServerLoop to stop -- handling requests without exiting the process. [StopServer] :: Interrupt r -> CallbackResult r -- | Serve an Api in a newly spawned process. spawnApiServer :: forall api eff. (ToServerPids api, HasCallStack, Member Logs eff, PrettyTypeShow (PrettyServerPids api)) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Serve an Api in a newly spawned -and linked - process. spawnLinkApiServer :: forall api eff. (ToServerPids api, HasCallStack, Member Logs eff, Typeable api, PrettyTypeShow (PrettyServerPids api)) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; the callbacks -- have access to a state value. -- -- The initial state value is returned by the action given as parameter, -- from within the new process. spawnApiServerStateful :: forall api eff state. (HasCallStack, ToServerPids api, NFData state, PrettyTypeShow (PrettyServerPids api)) => Eff (InterruptableProcess eff) state -> MessageCallback api (State state : InterruptableProcess eff) -> InterruptCallback (State state : ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. spawnApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff, Member Logs serverEff, PrettyTypeShow (PrettyServerPids api)) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. Links to the calling process like linkProcess would. spawnLinkApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff, Member Logs serverEff, PrettyTypeShow (PrettyServerPids api)) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | A smart constructor for MessageCallbacks handleMessages :: forall eff a. (HasCallStack, NFData a, Typeable a) => (a -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleSelectedMessages :: forall eff a. HasCallStack => MessageSelector a -> (a -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleAnyMessages :: forall eff. HasCallStack => (StrictDynamic -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleCasts :: forall api eff. (HasCallStack, Typeable api, Typeable (Api api 'Asynchronous), NFData (Request api)) => (Api api 'Asynchronous -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks -- --

Example

-- --
--   handleCalls
--     ( (RentBook bookId customerId) runCall ->
--        runCall $ do
--            rentalIdE <- rentBook bookId customerId
--            case rentalIdE of
--              -- on fail we just don't send a reply, let the caller run into
--              -- timeout
--              Left err -> return (Nothing, AwaitNext)
--              Right rentalId -> return (Just rentalId, AwaitNext))
--   
handleCalls :: forall api eff effScheduler. (HasCallStack, Typeable api, SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (forall secret reply. (NFData reply, Typeable reply, Typeable (Api api ( 'Synchronous reply))) => Api api ( 'Synchronous reply) -> (Eff eff (Maybe reply, CallbackResult 'Recoverable) -> secret) -> secret) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleCastsAndCalls :: forall api eff effScheduler. (HasCallStack, Typeable api, Typeable (Api api 'Asynchronous), NFData (Request api), SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (Api api 'Asynchronous -> Eff eff (CallbackResult 'Recoverable)) -> (forall secret reply. (Typeable reply, Typeable (Api api ( 'Synchronous reply)), NFData reply) => Api api ( 'Synchronous reply) -> (Eff eff (Maybe reply, CallbackResult 'Recoverable) -> secret) -> secret) -> MessageCallback api eff -- | A variation of handleCalls that allows to defer a reply to a -- call. handleCallsDeferred :: forall api eff effScheduler. (HasCallStack, Typeable api, SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (forall reply. (Typeable reply, NFData reply, Typeable (Api api ( 'Synchronous reply))) => RequestOrigin (Api api ( 'Synchronous reply)) -> Api api ( 'Synchronous reply) -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleProcessDowns :: forall eff. HasCallStack => (MonitorReference -> Eff eff (CallbackResult 'Recoverable)) -> MessageCallback '[] eff -- | Compose two Apis to a type-level pair of them. -- --
--   handleCalls api1calls ^: handleCalls api2calls ^:
--   
(^:) :: forall (api1 :: Type) (apis2 :: [Type]) eff. HasCallStack => MessageCallback api1 eff -> MessageCallback apis2 eff -> MessageCallback (api1 : apis2) eff infixr 5 ^: -- | Make a fallback handler, i.e. a handler to which no other can be -- composed to from the right. fallbackHandler :: forall api eff. HasCallStack => MessageCallback api eff -> MessageCallback '[] eff -- | A fallbackHandler that drops the left-over messages. dropUnhandledMessages :: forall eff. HasCallStack => MessageCallback '[] eff -- | A fallbackHandler that terminates if there are unhandled -- messages. exitOnUnhandled :: forall eff. HasCallStack => MessageCallback '[] eff -- | A fallbackHandler that drops the left-over messages. logUnhandledMessages :: forall eff. (Member Logs eff, HasCallStack) => MessageCallback '[] eff -- | A smart constructor for InterruptCallbacks stopServerOnInterrupt :: forall eff. HasCallStack => InterruptCallback eff -- | Wraps the source ProcessId and a unique identifier for a -- Call. data RequestOrigin request RequestOrigin :: !ProcessId -> !Int -> RequestOrigin request [_requestOriginPid] :: RequestOrigin request -> !ProcessId [_requestOriginCallRef] :: RequestOrigin request -> !Int -- | The wrapper around replies to Calls. data Reply request [Reply] :: (Typeable api, Typeable reply, NFData reply) => Proxy (Api api ( 'Synchronous reply)) -> Int -> reply -> Reply (Api api ( 'Synchronous reply)) -- | A wrapper sum type for calls and casts for the methods of an -- Api subtype data Request api [Call] :: forall api reply. (Typeable api, Typeable reply, NFData reply, Typeable (Api api ( 'Synchronous reply)), NFData (Api api ( 'Synchronous reply))) => Int -> ProcessId -> Api api ( 'Synchronous reply) -> Request api [Cast] :: forall api. (Typeable api, Typeable (Api api 'Asynchronous), NFData (Api api 'Asynchronous)) => Api api 'Asynchronous -> Request api -- | TODO remove mkRequestOrigin :: request -> ProcessId -> Int -> RequestOrigin request -- | Send a Reply to a Call. -- -- The reply will be deeply evaluated to rnf. sendReply :: forall request reply api eff q. (SetMember Process (Process q) eff, Member Interrupts eff, Typeable api, ApiType request ~ api, ReplyType request ~ reply, request ~ Api api ( 'Synchronous reply), Typeable reply, NFData reply) => RequestOrigin request -> reply -> Eff eff () -- | This data family defines an API, a communication interface description -- between at least two processes. The processes act as servers or -- client(s) regarding a specific instance of this type. -- -- The first parameter is usually a user defined phantom type that -- identifies the Api instance. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Api instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Also, for better logging, the an instance of ToPretty for the -- Api index type must be given. -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   data instance Api BookShop r where
--     RentBook  :: BookId   -> Api BookShop ('Synchronous (Either RentalError RentalId))
--     BringBack :: RentalId -> Api BookShop 'Asynchronous
--   
--   type instance ToPretty BookShop = PutStr "book shop"
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
data family Api (api :: Type) (reply :: Synchronicity) -- | Alias for the effect that contains the observers managed by -- manageObservers type ObserverState o = State (Observers o) -- | An Api for managing Observers, encompassing registration -- and de-registration of Observers. data ObserverRegistry o -- | Describes a process that observes another via Asynchronous -- Api messages. -- -- An observer consists of a filter and a process id. The filter converts -- an observation to a message understood by the observer process, and -- the ProcessId is used to send the message. data Observer o [Observer] :: (PrettyTypeShow (ToPretty p), Show (Server p), Typeable p, Typeable o, NFData o, NFData (Api p 'Asynchronous)) => (o -> Maybe (Api p 'Asynchronous)) -> Server p -> Observer o -- | And an Observer to the set of recipients for all observations -- reported by observed. Note that the observers are keyed by the -- observing process, i.e. a previous entry for the process contained in -- the Observer is overwritten. If you want multiple entries for a -- single process, just combine several filter functions. registerObserver :: (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o, NFData o, PrettyTypeShow (ToPretty o)) => Observer o -> Server (ObserverRegistry o) -> Eff r () -- | Send the ForgetObserver message forgetObserver :: (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o, NFData o, PrettyTypeShow (ToPretty o)) => Observer o -> Server (ObserverRegistry o) -> Eff r () -- | Based on the Api instance for Observer this simplified -- writing a callback handler for observations. In order to register to -- and ObserverRegistry use toObserver. handleObservations :: (HasCallStack, Typeable o, SetMember Process (Process q) r, NFData (Observer o)) => (o -> Eff r (CallbackResult 'Recoverable)) -> MessageCallback (Observer o) r -- | Use a Server as an Observer for -- handleObservations. toObserver :: (NFData o, Typeable o, NFData (Api (Observer o) 'Asynchronous), PrettyTypeShow (ToPretty o)) => Server (Observer o) -> Observer o -- | Create an Observer that conditionally accepts all observations -- of the given type and applies the given function to them; the function -- takes an observation and returns an Api cast that the observer -- server is compatible to. toObserverFor :: (Typeable a, PrettyTypeShow (ToPretty a), NFData (Api a 'Asynchronous), Typeable o, NFData o) => (o -> Api a 'Asynchronous) -> Server a -> Observer o -- | Provide the implementation for the ObserverRegistry Api, this -- handled RegisterObserver and ForgetObserver messages. It -- also adds the ObserverState constraint to the effect list. handleObserverRegistration :: forall o q r. (HasCallStack, Typeable o, SetMember Process (Process q) r, Member (ObserverState o) r, Member Logs r) => MessageCallback (ObserverRegistry o) r -- | Keep track of registered Observers. -- -- Handle the ObserverState introduced by -- handleObserverRegistration. manageObservers :: Eff (ObserverState o : r) a -> Eff r a -- | Report an observation to all observers. The process needs to -- manageObservers and to handleObserverRegistration. observed :: forall o r q. (SetMember Process (Process q) r, PrettyTypeShow (ToPretty o), Member (ObserverState o) r, Member Interrupts r) => o -> Eff r () -- | A Reader for an ObservationQueue. type ObservationQueueReader a = Reader (ObservationQueue a) -- | Contains a TBQueue capturing observations. See -- spawnLinkObservationQueueWriter, readObservationQueue. data ObservationQueue a -- | Read queued observations captured and enqueued in the shared -- TBQueue by spawnLinkObservationQueueWriter. This blocks -- until something was captured or an interrupt or exceptions was thrown. -- For a non-blocking variant use tryReadObservationQueue or -- flushObservationQueue. readObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r o -- | Read queued observations captured and enqueued in the shared -- TBQueue by spawnLinkObservationQueueWriter. Return the -- oldest enqueued observation immediately or Nothing if the queue -- is empty. Use readObservationQueue to block until an -- observation is observed. tryReadObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r (Maybe o) -- | Read at once all currently queued observations captured and enqueued -- in the shared TBQueue by -- spawnLinkObservationQueueWriter. This returns immediately all -- currently enqueued observations. For a blocking variant use -- readObservationQueue. flushObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r [o] -- | Create a mutable queue for observations. Use -- spawnLinkObservationQueueWriter for a simple way to get a -- process that enqueues all observations. -- --

Example

-- --
--   withObservationQueue 100 $ do
--     q  <- ask @(ObservationQueueReader TestEvent)
--     wq <- spawnLinkObservationQueueWriter q
--     registerObserver wq testServer
--     ...
--     cast testServer DoSomething
--     evt <- readObservationQueue @TestEvent
--     ...
--   
withObservationQueue :: forall o b e len. (HasCallStack, Typeable o, Show o, Member Logs e, Lifted IO e, Integral len, Member Interrupts e) => len -> Eff (ObservationQueueReader o : e) b -> Eff e b -- | Spawn a process that can be used as an Observer that enqueues -- the observations into an ObservationQueue. See -- withObservationQueue for an example. -- -- The observations can be obtained by readObservationQueue. All -- observations are captured up to the queue size limit, such that the -- first message received will be first message returned by -- readObservationQueue. spawnLinkObservationQueueWriter :: forall o q. (Tangible o, NFData (Api (Observer o) 'Asynchronous), Member Logs q, Lifted IO q, HasCallStack) => ObservationQueue o -> Eff (InterruptableProcess q) (Observer o) -- | The concrete list of Effects for this scheduler implementation. type SchedulerIO = (Reader SchedulerState : LoggingAndIo) -- | Type class constraint to indicate that an effect union contains the -- effects required by every process and the scheduler implementation -- itself. type HasSchedulerIO r = (HasCallStack, Lifted IO r, SchedulerIO <:: r) -- | The concrete list of the effects, that the Process uses type InterruptableProcEff = InterruptableProcess SchedulerIO -- | The concrete list of Effects of processes compatible with this -- scheduler. This builds upon SchedulerIO. type ProcEff = ConsProcess SchedulerIO -- | Start the message passing concurrency system then execute a -- Process on top of SchedulerIO effect. All logging is -- sent to standard output. defaultMain :: HasCallStack => Eff InterruptableProcEff () -> IO () -- | Start the message passing concurrency system then execute a -- Process on top of SchedulerIO effect. All logging is -- sent to standard output. defaultMainWithLogWriter :: HasCallStack => LogWriter IO -> Eff InterruptableProcEff () -> IO () -- | This is the main entry point to running a message passing concurrency -- application. This function takes a Process on top of the -- SchedulerIO effect for concurrent logging. schedule :: HasCallStack => Eff InterruptableProcEff () -> Eff LoggingAndIo () -- | Like scheduleIO but pure. The yield effect is -- just return (). schedulePure == runIdentity . -- scheduleM (Identity . run) (return ()) schedulePure :: Eff (InterruptableProcess '[Logs, LogWriterReader PureLogWriter]) a -> Either (Interrupt 'NoRecovery) a -- | Execute a Process using scheduleM on top of Lift -- IO and withLogging String effects. defaultMainSingleThreaded :: HasCallStack => Eff (InterruptableProcess LoggingAndIo) () -> IO ()