-- 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.32.0 -- | Internal module containing internal helpers that didn't make it into -- their own library. module Control.Eff.Concurrent.Misc -- | An internal utility to print Typeable without the kinds. This -- is like showsPrec in that it accepts a precedence -- parameter, and the result is in parentheses when the precedence is -- higher than 9. showSTypeRepPrec :: Int -> SomeTypeRep -> ShowS -- | This is equivalent to showSTypeRepPrec 0 showSTypeRep :: SomeTypeRep -> ShowS -- | Render a Typeable to a ShowS. showSTypeable :: forall message. Typeable message => ShowS -- | Render a Typeable to a ShowS with a precedence -- parameter. showSPrecTypeable :: forall message. Typeable message => Int -> ShowS -- | 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 -- | LogMessage to Text rendering function. type LogMessageTextRenderer = LogMessageRenderer Text -- | 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 :: LogMessageTextRenderer -- | Render a LogMessage human readable, for console logging renderLogMessageConsoleLog :: LogMessageTextRenderer -- | Render a LogMessage human readable, for console logging renderConsoleMinimalisticWide :: LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-3164. renderRFC3164 :: LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-3164 but -- use RFC5424 time stamps. renderRFC3164WithRFC5424Timestamps :: LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-3164 but -- use the custom LogMessageTimeRenderer. renderRFC3164WithTimestamp :: LogMessageTimeRenderer -> LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-5424. -- -- Equivalent to renderRFC5424Header <> const " " -- <> renderLogMessageBody. renderRFC5424 :: LogMessageTextRenderer -- | Render the header and strucuted data of a LogMessage according -- to the rules in the RFC-5424, but do not render the lmMessage. renderRFC5424Header :: LogMessageTextRenderer -- | 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 :: LogMessageTextRenderer -- | Render the severity and facility as described in RFC-3164 -- -- Render e.g. as <192>. -- -- Useful as header for syslog compatible log output. renderSyslogSeverityAndFacility :: LogMessageTextRenderer -- | 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) -> LogMessageTextRenderer -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBodyNoLocation :: LogMessageTextRenderer -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBody :: LogMessageTextRenderer -- | Print the body of a LogMessage with fix size fields (60) -- for the message itself and 30 characters for the location renderLogMessageBodyFixWidth :: LogMessageTextRenderer -- | 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 IO action to write -- LogMessages. module Control.Eff.Log.Writer -- | A function that takes a log message and returns an effect that -- logs the message. newtype LogWriter MkLogWriter :: (LogMessage -> IO ()) -> LogWriter [runLogWriter] :: LogWriter -> LogMessage -> IO () -- | A Reader effect for LogWriters. type LogWriterReader = Reader LogWriter -- | Modify the current LogWriter. localLogWriterReader :: forall e a. Member LogWriterReader e => (LogWriter -> LogWriter) -> Eff e a -> Eff e a -- | Get the current LogWriter. askLogWriter :: Member LogWriterReader e => Eff e LogWriter -- | Provide the LogWriter -- -- Exposed for custom extensions, if in doubt use withLogging. runLogWriterReader :: LogWriter -> Eff (Reader LogWriter : e) a -> Eff e a -- | Write a message using the LogWriter found in the environment. liftWriteLogMessage :: (Member LogWriterReader e, Lifted IO e) => LogMessage -> Eff e () -- | This LogWriter will discard all messages. -- -- NOTE: This is just an alias for mempty noOpLogWriter :: LogWriter -- | A LogWriter that applies a predicate to the LogMessage -- and delegates to to the given writer of the predicate is satisfied. filteringLogWriter :: LogPredicate -> LogWriter -> LogWriter -- | A LogWriter that applies a function to the LogMessage -- and delegates the result to to the given writer. mappingLogWriter :: (LogMessage -> LogMessage) -> LogWriter -> LogWriter -- | Like mappingLogWriter allow the function that changes the -- LogMessage to have effects. mappingLogWriterIO :: (LogMessage -> IO LogMessage) -> LogWriter -> LogWriter -- | Append the LogMessage to an Handle after rendering it. ioHandleLogWriter :: Handle -> LogMessageRenderer Text -> LogWriter -- | Render a LogMessage to stdout. -- -- This function will also set the BufferMode of stdout to -- LineBuffering. -- -- See ioHandleLogWriter. stdoutLogWriter :: LogMessageRenderer Text -> IO LogWriter instance GHC.Base.Semigroup Control.Eff.Log.Writer.LogWriter instance GHC.Base.Monoid Control.Eff.Log.Writer.LogWriter -- | 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 () -- | Log the current callStack using the given Severity. logCallStack :: forall e. (HasCallStack, Member Logs e) => Severity -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multi line log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. logMultiLine :: forall e. (HasCallStack, Member Logs e) => Severity -> [Text] -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multiline log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. -- -- This function takes a list of Strings as opposed to -- logMultiLine. logMultiLine' :: forall e. (HasCallStack, Member Logs e) => Severity -> [String] -> Eff e () -- | Include LogMessages that match a LogPredicate. -- -- includeLogMessages 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: -- --
--   exampleSetLogWriter :: IO Int
--   exampleSetLogWriter =
--       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 :: IoLogging e => LogWriter -> 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 :: IoLogging e => LogWriter -> Eff e a -> Eff e a -- | Change the current LogWriter. modifyLogWriter :: IoLogging e => (LogWriter -> LogWriter) -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriter censorLogs :: IoLogging 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 . -- mappingLogWriterIO censorLogsIo :: IoLogging e => (LogMessage -> IO 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 that requires Logs and LogWriterReader, and -- hence supports the functions to filter and modify logs: -- -- -- -- Provided by withLogging, runLogs, and also -- withoutLogging and runLogsWithoutLogging. type FilteredLogging e = (Member Logs e, Member LogWriterReader e) -- | A constraint that requires Logs e and -- Lifted IO e. -- -- Provided by withLogging and runLogs. -- -- It contains FilteredLogging and allows in addition: -- -- -- -- Don't infect everything with IO, if you can fall back to -- FilteredLogging. type IoLogging e = (FilteredLogging e, Lifted IO e) -- | The concrete list of Effects for logging with a -- LogWriter, and a LogWriterReader. -- -- This also provides both IoLogging and FilteredLogging. type LoggingAndIo = '[Logs, LogWriterReader, Lift IO] -- | 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"
--   
-- -- This provides the IoLogging and FilteredLogging effects. -- -- See also runLogs. withLogging :: Lifted IO e => LogWriter -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Handles the Logs and LogWriterReader effects, while not -- invoking the LogWriter at all. -- -- There is no way to get log output when this logger is used. -- -- Example: -- --
--   exampleWithSomeLogging :: ()
--   exampleWithSomeLogging =
--       run
--     $ withoutLogging
--     $ logDebug "Oh, hi there" -- Nothing written
--   
-- -- This provides the FilteredLogging effect. -- -- See also runLogsWithoutLogging. withoutLogging :: Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withLogging. runLogs :: forall e b. (Member LogWriterReader (Logs : e), Lifted IO e) => LogPredicate -> Eff (Logs : e) b -> Eff e b -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withoutLogging. runLogsWithoutLogging :: forall e b. Member LogWriterReader (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 GHC.Types.IO, Control.Monad.Trans.Control.MonadBaseControl GHC.Types.IO (Control.Eff.Internal.Eff e), Control.Eff.Internal.LiftedBase m e, Control.Eff.Internal.Lifted GHC.Types.IO e, Control.Eff.Log.Handler.IoLogging (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.Monad.Trans.Control.MonadBaseControl GHC.Types.IO (Control.Eff.Internal.Eff e), Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e), Control.Eff.Log.Handler.IoLogging (Control.Eff.Log.Handler.Logs : e), Control.Eff.Internal.Lifted GHC.Types.IO e) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) instance (GHC.Base.Applicative m, Control.Monad.Trans.Control.MonadBaseControl GHC.Types.IO (Control.Eff.Internal.Eff e), Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e), Control.Eff.Log.Handler.IoLogging (Control.Eff.Log.Handler.Logs : e), Control.Eff.Internal.Lifted GHC.Types.IO 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 ()) -- | Simply wait until the time in the given Timeout has elapsed and -- return. [Delay] :: Timeout -> 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] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | 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. -- -- You might wonder: Why not tearing down the linked process when exiting -- normally? I thought about this. If a process exits normally, it should -- have the opportunity to shutdown stuff explicitly. And if you want to -- make sure that there are no dangling child processes after e.g. a -- broker crash, you can always use monitor. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Update the ProcessDetails of a process [UpdateProcessDetails] :: ProcessDetails -> Process r (ResumeProcess ()) -- | Get the ProcessState (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe (ProcessTitle, ProcessDetails, ProcessState))) -- | Cons Process onto a list of effects. This is called -- SafeProcesses because the the actions cannot be interrupted -- in. type SafeProcesses r = Process r : r -- | This adds a layer of the Interrupts effect on top of -- Processes type Processes e = Interrupts : SafeProcesses e -- | A constraint for an effect set that requires the presence of -- Processes. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Interrupts, Process [e(N+1) .. e(N+k)], e(N+1) .. -- e(N+k)] -- -- It constrains e beyond HasSafeProcesses to encompass -- Interrupts. type HasProcesses e inner = (HasSafeProcesses e inner, Member Interrupts e) -- | A constraint for an effect set that requires the presence of -- SafeProcesses. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Process [e(N+1) .. e(N+k)], e(N+1) .. e(N+k)] -- -- It constrains e to support the (only) Process effect. -- -- This is more relaxed that HasProcesses since it does not -- require Interrupts. type HasSafeProcesses e inner = (SetMember Process (Process inner) e) -- | A short title for a Process for logging purposes. newtype ProcessTitle MkProcessTitle :: Text -> ProcessTitle [_fromProcessTitle] :: ProcessTitle -> Text -- | An isomorphism lens for the ProcessTitle fromProcessTitle :: Lens' ProcessTitle Text -- | A multi-line text describing the current state of a process for -- debugging purposes. newtype ProcessDetails MkProcessDetails :: Text -> ProcessDetails [_fromProcessDetails] :: ProcessDetails -> Text -- | An isomorphism lens for the ProcessDetails fromProcessDetails :: Lens' ProcessDetails Text -- | A number of micro seconds. newtype Timeout TimeoutMicros :: Int -> Timeout [fromTimeoutMicros] :: Timeout -> Int -- | 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 -- | Serialize a message into a StrictDynamic value to be -- sent via sendAnyMessage. -- -- This indirection allows, among other things, the composition of -- Servers. newtype Serializer message MkSerializer :: (message -> StrictDynamic) -> Serializer message [runSerializer] :: Serializer message -> message -> StrictDynamic -- | 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 -- | 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 a non-blocking operation ProcessBusy :: ProcessState -- | The process is sleeping until the Timeout given to Delay ProcessBusySleeping :: ProcessState -- | The process is busy with UpdateProcessDetails ProcessBusyUpdatingDetails :: 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. (HasProcesses r q, HasCallStack) => Eff r () -- | Simply block until the time in the Timeout has passed. delay :: forall r q. (HasProcesses r q, HasCallStack) => Timeout -> 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 o r q. (HasProcesses r q, HasCallStack, 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. (HasCallStack, HasProcesses r q) => ProcessId -> StrictDynamic -> Eff r () -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => 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, HasProcesses r q) => MessageSelector a -> Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, HasProcesses r q) => Eff r [StrictDynamic] -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, HasProcesses r q) => Eff r StrictDynamic -- | Like receiveSelectedLoop but refined to casting to a specific -- Typeable using selectMessage. receiveLoop :: forall r q a endOfLoopResult. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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, HasSafeProcesses r q) => Eff r ProcessId -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r Bool -- | Return the ProcessTitle, ProcessDetails and -- ProcessState, for the given process, if the process is alive. getProcessState :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r (Maybe (ProcessTitle, ProcessDetails, ProcessState)) -- | Replace the ProcessDetails of the process. updateProcessDetails :: forall r q. (HasCallStack, HasProcesses r q) => ProcessDetails -> 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, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r () -- | Start a new process, and immediately link to it. -- -- See Link for a discussion on linking. spawnLink :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes 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 SafeProcesses effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff 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 -- | 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasProcesses r q, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Throw an Interrupt, can be handled by handleInterrupts -- or exitOnInterrupt or provideInterrupts. interrupt :: (HasCallStack, Member Interrupts r) => Interrupt 'Recoverable -> Eff r a -- | 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. (HasCallStack, HasProcesses r q) => ProcessId -> Interrupt 'Recoverable -> Eff r () -- | Exit the process with a Interrupt. exitBecause :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Interrupt 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, HasSafeProcesses r q) => String -> Eff r a -- | 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. (HasCallStack, HasProcesses r q) => ProcessId -> Interrupt 'NoRecovery -> Eff r () -- | 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. -- -- See Link for a discussion on linking. linkProcess :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. unlinkProcess :: forall r q. (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, HasProcesses r q) => 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 -> !Interrupt 'NoRecovery -> !ProcessId -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !Interrupt 'NoRecovery [downProcess] :: ProcessDown -> !ProcessId -- | A MessageSelector for the ProcessDown message of a -- specific process. -- -- The parameter is the value obtained by monitor. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | A MessageSelector for the ProcessDown message. of a -- specific process. -- -- In contrast to selectProcessDown this function matches the -- ProcessId. selectProcessDownByProcessId :: ProcessId -> 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, HasProcesses r q) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, HasProcesses r q, Typeable a, Show a) => ProcessId -> MessageSelector a -> Eff r (Either ProcessDown a) -- | 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 -- | Extension of ExitNormally with a custom reason [NormalExitRequestedWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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, see Link for a discussion on linking. [LinkedProcessCrashed] :: ProcessId -> Interrupt 'Recoverable -- | An exit reason that has an error message and is Recoverable. [ErrorInterrupt] :: String -> Interrupt 'Recoverable -- | An interrupt with a custom message. [InterruptedBy] :: forall a. (Typeable a, Show a, NFData a) => a -> Interrupt 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: Interrupt 'NoRecovery -- | A process function returned or exited without any error, and with a -- custom message [ExitNormallyWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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 -- recovered. [ExitUnhandledError] :: Text -> Interrupt 'NoRecovery -- | A process shall exit immediately, without any cleanup was cancelled -- (e.g. killed, in cancel) [ExitProcessCancelled] :: Maybe ProcessId -> Interrupt 'NoRecovery -- | A process that is vital to the crashed process was not running [ExitOtherProcessNotRunning] :: ProcessId -> Interrupt 'NoRecovery -- | Exceptions containing Interrupts. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc (Interrupt 'Recoverable) -- | 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 -- | 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 () -- | Handle all Interrupts of an Processes by wrapping them -- up in interruptToExit and then do a process Shutdown. provideInterruptsShutdown :: forall e a. Eff (Processes e) a -> Eff (SafeProcesses 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, HasProcesses r q) => 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 -- | Serialize and send a message to the process in a Receiver. -- -- EXPERIMENTAL sendToReceiver :: (NFData o, HasProcesses r q) => Receiver o -> o -> Eff r () -- | A ProcessId and a Serializer. EXPERIMENTAL -- -- See sendToReceiver. data Receiver a Receiver :: ProcessId -> (a -> out) -> Receiver a [_receiverPid] :: Receiver a -> ProcessId [_receiverSerializer] :: Receiver a -> a -> out receiverPid :: forall a_aHaF. Lens' (Receiver a_aHaF) ProcessId instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Process.Receiver o) instance GHC.Classes.Eq (Control.Eff.Concurrent.Process.Receiver o) instance GHC.Classes.Ord (Control.Eff.Concurrent.Process.Receiver o) instance Data.Functor.Contravariant.Contravariant Control.Eff.Concurrent.Process.Receiver instance Data.Typeable.Internal.Typeable protocol => GHC.Show.Show (Control.Eff.Concurrent.Process.Receiver protocol) 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.Enum.Enum Control.Eff.Concurrent.Process.Timeout instance GHC.Real.Real Control.Eff.Concurrent.Process.Timeout instance GHC.Real.Integral Control.Eff.Concurrent.Process.Timeout instance GHC.Num.Num Control.Eff.Concurrent.Process.Timeout instance GHC.Classes.Eq Control.Eff.Concurrent.Process.Timeout instance GHC.Classes.Ord Control.Eff.Concurrent.Process.Timeout instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.Timeout instance GHC.Base.Monoid Control.Eff.Concurrent.Process.ProcessDetails instance GHC.Base.Semigroup Control.Eff.Concurrent.Process.ProcessDetails instance Data.String.IsString Control.Eff.Concurrent.Process.ProcessDetails instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ProcessDetails instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessDetails instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessDetails instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessDetails instance GHC.Base.Monoid Control.Eff.Concurrent.Process.ProcessTitle instance GHC.Base.Semigroup Control.Eff.Concurrent.Process.ProcessTitle instance Data.String.IsString Control.Eff.Concurrent.Process.ProcessTitle instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ProcessTitle instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessTitle instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessTitle instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessTitle 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 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 Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessDown instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessDown 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 Control.DeepSeq.NFData (Control.Eff.Concurrent.Process.Serializer message) instance Data.Typeable.Internal.Typeable message => GHC.Show.Show (Control.Eff.Concurrent.Process.Serializer message) instance Data.Functor.Contravariant.Contravariant Control.Eff.Concurrent.Process.Serializer instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.StrictDynamic instance GHC.Show.Show Control.Eff.Concurrent.Process.StrictDynamic instance GHC.Show.Show Control.Eff.Concurrent.Process.Timeout instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessDetails instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessTitle -- | Types and functions for type-safe(er) interaction between processes. -- -- All messages sent between processes are eventually converted to -- Dynamic values which carry little type information. -- -- A step towards a more controlled and type safe process interaction -- model is done with the facilities defined in this module. -- -- The metaphor for communication is a stateless protocol that -- describes the messages handled by a process. -- -- A protocol is represented by a custom data type, often a -- phantom type, which is then used to form specific instances of -- type classes data/type families, to determine the messages, the -- replies, the servers and clients, associated with specific task, that -- needs to be executed concurrently. -- -- This module contains a mechanism to specify what kind of messages (aka -- requests) an Endpoint can handle. -- -- The Endpoint wraps a ProcessId and carries the protocol -- phantom-type, to indicate the messages that a process repsonds to. -- -- The associated data type Pdu defines the messages or -- 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 protocol by using the functions -- provided by the Control.Eff.Concurrent.Protocol.EffectfulServer -- and Control.Eff.Concurrent.Protocol.EffectfulServer modules. -- -- To enable a process to use such a service, the functions -- provided in Control.Eff.Concurrent.Protocol.Client should be -- used. module Control.Eff.Concurrent.Protocol -- | This type class and the associated data family defines the protocol -- data units (PDU) of a protocol. -- -- A Protocol in the sense of a communication interface description -- between processes. -- -- The first parameter is usually a user defined type that identifies the -- protocol that uses the Pdus are. It maybe a phantom -- type. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Pdu instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   instance Typeable r => HasPdu BookShop r where
--     data instance Pdu BookShop r where
--       RentBook  :: BookId   -> Pdu BookShop ('Synchronous (Either RentalError RentalId))
--       BringBack :: RentalId -> Pdu BookShop 'Asynchronous
--       deriving Typeable
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
class Typeable protocol => HasPdu (protocol :: Type) where { -- | A type level list Protocol phantom types included in the associated -- Pdu instance. -- -- This is just a helper for better compiler error messages. It relies on -- Embeds to add the constraint HasPduPrism. type family EmbeddedPduList protocol :: [Type]; -- | The protocol data unit type for the given protocol. data family Pdu protocol (reply :: Synchronicity); type EmbeddedPduList protocol = '[]; } -- | Deserialize a Pdu from a Dynamic i.e. from a message -- received by a process. deserializePdu :: Typeable (Pdu protocol reply) => Dynamic -> Maybe (Pdu protocol reply) -- | A constraint that requires that the outer Pdu has a -- clause to embed values from the inner Pdu. -- -- Also, this constraint requires a HasPduPrism instance, as a -- proof for a possible conversion of an embedded Pdu value into -- to the enclosing Pdu. -- -- This generates better compiler error messages, when an embedding of a -- Pdu into another. -- -- This is provided by HasPdu instances. The instances are -- required to provide a list of embedded Pdu values in -- EmbeddedPduList. -- -- Note that every type embeds itself, so Embeds x x always -- holds. type Embeds outer inner = (HasPduPrism outer inner, CheckEmbeds outer inner, HasPdu outer) -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Pdu -- 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 -- | This type function takes an Pdu and analysis the reply type, -- i.e. the Synchronicity and evaluates to either t for -- an Pdu x (Synchronous t) or to '()' for an Pdu x -- Asynchronous. type family ProtocolReply (s :: 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. type Tangible i = (NFData i, Typeable i, Show i) -- | A Constraint that bundles the requirements for the Pdu -- values of a protocol. -- -- This ensures that Pdus can be strictly and deeply evaluated and -- shown such that for example logging is possible. type TangiblePdu p r = (Typeable p, Typeable r, Tangible (Pdu p r), HasPdu p) -- | A server process for protocol. -- -- Protocols are represented by phantom types, which are used in -- different places to index type families and type class instances. -- -- A Process can send and receive any messages. An Endpoint -- wraps around a ProcessId and carries a phantom type to indicate -- the kinds of messages accepted by the process. -- -- As a metaphor, communication between processes can be thought of -- waiting for and sending protocol data units belonging to some -- protocol. newtype Endpoint protocol Endpoint :: ProcessId -> Endpoint protocol [_fromEndpoint] :: Endpoint protocol -> ProcessId fromEndpoint :: forall protocol_aUDz protocol_aVLX. Iso (Endpoint protocol_aUDz) (Endpoint protocol_aVLX) ProcessId ProcessId -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API proxyAsEndpoint :: proxy protocol -> ProcessId -> Endpoint protocol -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API asEndpoint :: forall protocol. ProcessId -> Endpoint protocol -- | A class for Pdu instances that embed other Pdu. -- -- This is a part of Embeds provide instances for your Pdus -- but in client code use the Embeds constraint. -- -- Instances of this class serve as proof to Embeds that a -- conversion into another Pdu actually exists. -- -- A Prism for the embedded Pdu is the center of this class -- -- Laws: embeddedPdu = prism' embedPdu fromPdu class (Typeable protocol, Typeable embeddedProtocol) => HasPduPrism protocol embeddedProtocol -- | A Prism for the embedded Pdus. embeddedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Prism' (Pdu protocol result) (Pdu embeddedProtocol result) -- | Embed the Pdu value of an embedded protocol into the -- corresponding Pdu value. embedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu embeddedProtocol result -> Pdu protocol result -- | Examine a Pdu value from the outer protocol, and return it, if -- it embeds a Pdu of embedded protocol, otherwise return -- Nothing/ fromPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu protocol result -> Maybe (Pdu embeddedProtocol result) -- | Convert an Endpoint to an endpoint for an embedded protocol. -- -- See Embeds, fromEmbeddedEndpoint. toEmbeddedEndpoint :: forall inner outer. Embeds outer inner => Endpoint outer -> Endpoint inner -- | Convert an Endpoint to an endpoint for a server, that embeds -- the protocol. -- -- See Embeds, toEmbeddedEndpoint. fromEmbeddedEndpoint :: forall outer inner. HasPduPrism outer inner => Endpoint inner -> Endpoint outer instance forall k (protocol :: k). Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Endpoint protocol) instance forall k (protocol :: k). GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Endpoint protocol) instance forall k (protocol :: k). GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Endpoint protocol) instance Data.Typeable.Internal.Typeable a => Control.Eff.Concurrent.Protocol.HasPduPrism a a instance (Data.Typeable.Internal.Typeable a1, Data.Typeable.Internal.Typeable a2) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2) a1 instance (Data.Typeable.Internal.Typeable a1, Data.Typeable.Internal.Typeable a2) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2) a2 instance (Data.Typeable.Internal.Typeable a1, Data.Typeable.Internal.Typeable a2, Data.Typeable.Internal.Typeable a3) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3) a1 instance (Data.Typeable.Internal.Typeable a1, Data.Typeable.Internal.Typeable a2, Data.Typeable.Internal.Typeable a3) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3) a2 instance (Data.Typeable.Internal.Typeable a1, Data.Typeable.Internal.Typeable a2, Data.Typeable.Internal.Typeable a3) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3) a3 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4) a1 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4) a2 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4) a3 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4) a4 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4, Control.Eff.Concurrent.Protocol.HasPdu a5) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4, a5) a1 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4, Control.Eff.Concurrent.Protocol.HasPdu a5) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4, a5) a2 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4, Control.Eff.Concurrent.Protocol.HasPdu a5) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4, a5) a3 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4, Control.Eff.Concurrent.Protocol.HasPdu a5) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4, a5) a4 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4, Control.Eff.Concurrent.Protocol.HasPdu a5) => Control.Eff.Concurrent.Protocol.HasPduPrism (a1, a2, a3, a4, a5) a5 instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2) => Control.Eff.Concurrent.Protocol.HasPdu (a1, a2) instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3) => Control.Eff.Concurrent.Protocol.HasPdu (a1, a2, a3) instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4) => Control.Eff.Concurrent.Protocol.HasPdu (a1, a2, a3, a4) instance (Control.Eff.Concurrent.Protocol.HasPdu a1, Control.Eff.Concurrent.Protocol.HasPdu a2, Control.Eff.Concurrent.Protocol.HasPdu a3, Control.Eff.Concurrent.Protocol.HasPdu a4, Control.Eff.Concurrent.Protocol.HasPdu a5) => Control.Eff.Concurrent.Protocol.HasPdu (a1, a2, a3, a4, a5) instance (Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a1 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a2 r)) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (a1, a2) r) instance (GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a1 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a2 r)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (a1, a2) r) instance (Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a1 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a2 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a3 r)) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (a1, a2, a3) r) instance (GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a1 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a2 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a3 r)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (a1, a2, a3) r) instance (Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a1 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a2 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a3 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a4 r)) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (a1, a2, a3, a4) r) instance (GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a1 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a2 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a3 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a4 r)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (a1, a2, a3, a4) r) instance (Data.Typeable.Internal.Typeable r, Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a1 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a2 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a3 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a4 r), Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu a5 r)) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (a1, a2, a3, a4, a5) r) instance (GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a1 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a2 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a3 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a4 r), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu a5 r)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (a1, a2, a3, a4, a5) r) instance forall k (protocol :: k). Data.Typeable.Internal.Typeable protocol => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Endpoint protocol) -- | Proxies and containers for casts and calls. module Control.Eff.Concurrent.Protocol.Wrapper -- | A wrapper sum type for calls and casts for the Pdus of a -- protocol data Request protocol [Call] :: forall protocol reply. (Tangible reply, TangiblePdu protocol ( 'Synchronous reply)) => RequestOrigin protocol reply -> Pdu protocol ( 'Synchronous reply) -> Request protocol [Cast] :: forall protocol. (TangiblePdu protocol 'Asynchronous, NFData (Pdu protocol 'Asynchronous)) => Pdu protocol 'Asynchronous -> Request protocol -- | Answer a Call by sending the reply value to the client process. -- -- The ProcessId, the RequestOrigin and the Reply -- Serializer are stored in the ReplyTarget. sendReply :: (HasProcesses eff q, Tangible reply, Typeable protocol) => ReplyTarget protocol reply -> reply -> Eff eff () -- | Target of a Call reply. -- -- This combines a RequestOrigin with a Serializer for a -- Reply using Arg. There are to smart constructors for -- this type: replyTarget and embeddedReplyTarget. -- -- Because of Arg the Eq and Ord instances are -- implemented via the RequestOrigin instances. newtype ReplyTarget p r MkReplyTarget :: Arg (RequestOrigin p r) (Serializer (Reply p r)) -> ReplyTarget p r -- | Smart constructor for a ReplyTarget. -- -- To build a ReplyTarget for an Embeds instance use -- embeddedReplyTarget. replyTarget :: Serializer (Reply p reply) -> RequestOrigin p reply -> ReplyTarget p reply -- | A simple Lens for the RequestOrigin of a -- ReplyTarget. replyTargetOrigin :: Lens' (ReplyTarget p reply) (RequestOrigin p reply) -- | A simple Lens for the Reply Serializer of a -- ReplyTarget. replyTargetSerializer :: Lens' (ReplyTarget p reply) (Serializer (Reply p reply)) -- | Smart constructor for an embedded ReplyTarget. -- -- This combines replyTarget and toEmbeddedReplyTarget. embeddedReplyTarget :: Embeds outer inner => Serializer (Reply outer reply) -> RequestOrigin outer reply -> ReplyTarget inner reply -- | Convert a ReplyTarget to be usable for embedded replies. -- -- This combines a toEmbeddedOrigin with -- embedReplySerializer to produce a ReplyTarget that can -- be passed to functions defined soley on an embedded protocol. toEmbeddedReplyTarget :: Embeds outer inner => ReplyTarget outer reply -> ReplyTarget inner reply -- | Wraps the source ProcessId and a unique identifier for a -- Call. data RequestOrigin (proto :: Type) reply RequestOrigin :: !ProcessId -> !Int -> RequestOrigin reply [_requestOriginPid] :: RequestOrigin reply -> !ProcessId [_requestOriginCallRef] :: RequestOrigin reply -> !Int -- | Turn an embedded RequestOrigin to a RequestOrigin -- for the bigger request. -- -- This is the inverse of toEmbeddedOrigin. -- -- This function is strict in all parameters. embedRequestOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin inner reply -> RequestOrigin outer reply -- | Turn an RequestOrigin to an origin for an embedded request (See -- Embeds). -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also embedReplySerializer. toEmbeddedOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin outer reply -> RequestOrigin inner reply -- | The wrapper around replies to Calls. data Reply protocol reply [Reply] :: Tangible reply => {_replyTo :: RequestOrigin protocol reply, _replyValue :: reply} -> Reply protocol reply -- | Turn a Serializer for a Pdu instance that contains -- embedded Pdu values into a Reply Serializer for -- the embedded Pdu. -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also toEmbeddedOrigin. embedReplySerializer :: forall outer inner reply. Embeds outer inner => Serializer (Reply outer reply) -> Serializer (Reply inner reply) -- | Create a new, unique RequestOrigin value for the current -- process. makeRequestOrigin :: (Typeable r, NFData r, HasProcesses e q0) => Eff e (RequestOrigin p r) instance GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Wrapper.ReplyTarget p r) instance GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Wrapper.ReplyTarget p r) instance forall proto k (reply :: k). GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Wrapper.RequestOrigin proto reply) instance forall proto k (reply :: k). GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Wrapper.RequestOrigin proto reply) instance forall proto k (reply :: k). GHC.Generics.Generic (Control.Eff.Concurrent.Protocol.Wrapper.RequestOrigin proto reply) instance GHC.Show.Show (Control.Eff.Concurrent.Protocol.Wrapper.ReplyTarget p r) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Wrapper.ReplyTarget p r) instance GHC.Show.Show (Control.Eff.Concurrent.Protocol.Wrapper.Request protocol) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Wrapper.Request protocol) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Wrapper.Reply p r) instance GHC.Show.Show r => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Wrapper.Reply p r) instance forall k p (r :: k). GHC.Show.Show (Control.Eff.Concurrent.Protocol.Wrapper.RequestOrigin p r) instance forall k p (r :: k). Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Wrapper.RequestOrigin p r) -- | Functions for receive timeouts and delayed messages sending. -- -- Based on the delay function. module Control.Eff.Concurrent.Process.Timer -- | 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. (HasCallStack, HasProcesses r q, 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. -- -- Calls sendAfter under the hood. startTimer :: forall r q. (HasCallStack, HasProcesses r q) => Timeout -> Eff r TimerReference -- | Like sendAfter but with a user provided name for the timer -- process. sendAfterWithTitle :: forall r q message. (HasCallStack, HasProcesses r q, Typeable message, NFData message) => ProcessTitle -> 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. -- -- This calls sendAfterWithTitle under the hood with -- TimerElapsed as message. startTimerWithTitle :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Timeout -> Eff r TimerReference -- | Cancel a timer started with startTimer. cancelTimer :: forall r q. (HasCallStack, HasProcesses r q) => 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. (HasCallStack, HasProcesses r q, 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. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitor combined with -- receiveSelectedAfter. receiveSelectedWithMonitorAfter :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> Eff r (Either (Either ProcessDown TimerElapsed) a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfterWithTitle. receiveAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Typeable a, NFData a, Show a) => Timeout -> ProcessTitle -> 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 startTimerWithTitle. receiveSelectedAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> ProcessTitle -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitorWithTitle combined with -- receiveSelectedAfterWithTitle. receiveSelectedWithMonitorAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> ProcessTitle -> 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.Show.Show Control.Eff.Concurrent.Process.Timer.TimerElapsed instance GHC.Show.Show Control.Eff.Concurrent.Process.Timer.TimerReference -- | FilteredLogging via extensible-effects -- -- FilteredLogging 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 () -- | Log the current callStack using the given Severity. logCallStack :: forall e. (HasCallStack, Member Logs e) => Severity -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multi line log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. logMultiLine :: forall e. (HasCallStack, Member Logs e) => Severity -> [Text] -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multiline log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. -- -- This function takes a list of Strings as opposed to -- logMultiLine. logMultiLine' :: forall e. (HasCallStack, Member Logs e) => Severity -> [String] -> Eff e () -- | Include LogMessages that match a LogPredicate. -- -- includeLogMessages 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: -- --
--   exampleSetLogWriter :: IO Int
--   exampleSetLogWriter =
--       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 :: IoLogging e => LogWriter -> 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 :: IoLogging e => LogWriter -> Eff e a -> Eff e a -- | Change the current LogWriter. modifyLogWriter :: IoLogging e => (LogWriter -> LogWriter) -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriter censorLogs :: IoLogging 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 . -- mappingLogWriterIO censorLogsIo :: IoLogging e => (LogMessage -> IO 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 that requires Logs and LogWriterReader, and -- hence supports the functions to filter and modify logs: -- -- -- -- Provided by withLogging, runLogs, and also -- withoutLogging and runLogsWithoutLogging. type FilteredLogging e = (Member Logs e, Member LogWriterReader e) -- | A constraint that requires Logs e and -- Lifted IO e. -- -- Provided by withLogging and runLogs. -- -- It contains FilteredLogging and allows in addition: -- -- -- -- Don't infect everything with IO, if you can fall back to -- FilteredLogging. type IoLogging e = (FilteredLogging e, Lifted IO e) -- | The concrete list of Effects for logging with a -- LogWriter, and a LogWriterReader. -- -- This also provides both IoLogging and FilteredLogging. type LoggingAndIo = '[Logs, LogWriterReader, Lift IO] -- | 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"
--   
-- -- This provides the IoLogging and FilteredLogging effects. -- -- See also runLogs. withLogging :: Lifted IO e => LogWriter -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Handles the Logs and LogWriterReader effects, while not -- invoking the LogWriter at all. -- -- There is no way to get log output when this logger is used. -- -- Example: -- --
--   exampleWithSomeLogging :: ()
--   exampleWithSomeLogging =
--       run
--     $ withoutLogging
--     $ logDebug "Oh, hi there" -- Nothing written
--   
-- -- This provides the FilteredLogging effect. -- -- See also runLogsWithoutLogging. withoutLogging :: Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withLogging. runLogs :: forall e b. (Member LogWriterReader (Logs : e), Lifted IO 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 -- | Utilities to implement effectful server-loops. module Control.Eff.Concurrent.Protocol.EffectfulServer -- | A type class for effectful server loops. -- -- This type class serves as interface for other abstractions, for -- example process supervision -- -- The methods of this class handle Events Requests for -- Pdu instance. -- -- Instances can by index types for Pdu family directly, or -- indirectly via the ServerPdu type family. -- -- To builder servers serving multiple protocols, use the generic -- Pdu instances, for which Embeds instances exist, like -- 2-,3-,4-, or 5-tuple. class Server (a :: Type) (e :: [Type -> Type]) where { -- | The value that defines what is required to initiate a Server -- loop. data family Init a; -- | The index type of the Events that this server processes. This -- is the first parameter to the Request and therefore of the -- Pdu family. type family ServerPdu a :: Type; -- | Effects of the implementation type family ServerEffects a e :: [Type -> Type]; type ServerPdu a = a; type ServerEffects a e = e; } -- | Return the ProcessTitle. -- -- Usually you should rely on the default implementation serverTitle :: Server a e => Init a -> ProcessTitle -- | Return the ProcessTitle. -- -- Usually you should rely on the default implementation serverTitle :: (Server a e, Typeable a) => Init a -> ProcessTitle -- | Process the effects of the implementation runEffects :: Server a e => Endpoint (ServerPdu a) -> Init a -> Eff (ServerEffects a e) x -> Eff e x -- | Process the effects of the implementation runEffects :: (Server a e, ServerEffects a e ~ e) => Endpoint (ServerPdu a) -> Init a -> Eff (ServerEffects a e) x -> Eff e x -- | Update the Model based on the Event. onEvent :: Server a e => Endpoint (ServerPdu a) -> Init a -> Event (ServerPdu a) -> Eff (ServerEffects a e) () -- | Update the Model based on the Event. onEvent :: (Server a e, Show (Init a), Member Logs (ServerEffects a e)) => Endpoint (ServerPdu a) -> Init a -> Event (ServerPdu a) -> Eff (ServerEffects a e) () -- | This event sum-type is used to communicate incoming messages and other -- events to the instances of Server. data Event a -- | A Synchronous message was received. If an implementation wants -- to delegate nested Pdus, it can use -- toEmbeddedReplyTarget to convert a ReplyTarget safely to -- the embedded protocol. [OnCall] :: forall a r. (Tangible r, TangiblePdu a ( 'Synchronous r)) => ReplyTarget a r -> Pdu a ( 'Synchronous r) -> Event a [OnCast] :: forall a. TangiblePdu a 'Asynchronous => Pdu a 'Asynchronous -> Event a [OnInterrupt] :: Interrupt 'Recoverable -> Event a [OnDown] :: ProcessDown -> Event a [OnTimeOut] :: TimerElapsed -> Event a [OnMessage] :: StrictDynamic -> Event a -- | Execute the server loop. start :: forall a r q. (Server a (Processes q), Typeable a, Typeable (ServerPdu a), FilteredLogging (Processes q), HasProcesses (ServerEffects a (Processes q)) q, HasProcesses r q, HasCallStack) => Init a -> Eff r (Endpoint (ServerPdu a)) -- | Execute the server loop. startLink :: forall a r q. (Typeable a, Typeable (ServerPdu a), Server a (Processes q), FilteredLogging (Processes q), HasProcesses (ServerEffects a (Processes q)) q, HasProcesses r q, HasCallStack) => Init a -> Eff r (Endpoint (ServerPdu a)) -- | Execute the server loop. protocolServerLoop :: forall q a. (Server a (Processes q), FilteredLogging (Processes q), HasProcesses (ServerEffects a (Processes q)) q, Typeable a, Typeable (ServerPdu a)) => Init a -> Eff (Processes q) () instance GHC.Show.Show (Control.Eff.Concurrent.Protocol.EffectfulServer.Event a) instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.EffectfulServer.Event a) -- | Utilities to implement server-loops with builtin state and -- TEA-like naming. module Control.Eff.Concurrent.Protocol.StatefulServer -- | A type class for server loops. -- -- This class serves as interface for other mechanisms, for example -- process supervision -- -- The methods of this class handle Events and Requests -- for Pdu instances. -- -- Instances can by index types for Pdu family directly, or -- indirectly via the ServerPdu type family. -- -- To builder servers serving multiple protocols, use the generic -- Pdu instances, for which Embeds instances exist, like -- 2-,3-,4-, or 5-tuple. -- -- The naming is inspired by The Elm Architecture, without the -- view callback. -- -- This class is based on -- Control.Eff.Concurrent.Protocol.EffectfulServer and adds a -- default State and Reader effect. class (Typeable (Protocol a)) => Server (a :: Type) q where { -- | The value that defines what is required to initiate a Server -- loop. data family StartArgument a; -- | The index type of the Events that this server processes. This -- is the first parameter to the Request and therefore of the -- Pdu family. type family Protocol a :: Type; -- | Type of the model data, given to every invocation of -- update via the ModelState effect. The model of a -- server loop is changed through incoming Events. It is -- initially calculated by setup. data family Model a :: Type; -- | Type of read-only state. type family Settings a :: Type; type Protocol a = a; type Settings a = (); } -- | Return a new ProcessTitle for the stateful process, while it is -- running. title :: Server a q => StartArgument a -> ProcessTitle -- | Return a new ProcessTitle for the stateful process, while it is -- running. title :: (Server a q, Typeable a) => StartArgument a -> ProcessTitle -- | Return an initial Model and Settings setup :: Server a q => Endpoint (Protocol a) -> StartArgument a -> Eff q (Model a, Settings a) -- | Return an initial Model and Settings setup :: (Server a q, Default (Model a), Default (Settings a)) => Endpoint (Protocol a) -> StartArgument a -> Eff q (Model a, Settings a) -- | Update the Model based on the Event. update :: Server a q => Endpoint (Protocol a) -> StartArgument a -> Event (Protocol a) -> Eff (ModelState a : (SettingsReader a : q)) () -- | This type is used to build stateful EffectfulServer -- instances. -- -- It is a variant of EffectfulServer, that comes pre-installed -- with State and Reader effects. data Stateful a -- | The value that defines what is required to initiate a Server -- loop. data family Init a -- | Execute the server loop. startLink :: forall a r q. (HasCallStack, Typeable a, FilteredLogging (Processes q), Server (Stateful a) (Processes q), Server a (Processes q), HasProcesses r q) => StartArgument a -> Eff r (Endpoint (Protocol a)) -- | Execute the server loop. Please use startLink if you can. start :: forall a r q. (HasCallStack, Typeable a, Server (Stateful a) (Processes q), Server a (Processes q), FilteredLogging (Processes q), HasProcesses r q) => StartArgument a -> Eff r (Endpoint (Protocol a)) -- | The Effect type of mutable Model in a Server -- instance. type ModelState a = State (Model a) -- | Modify the Model of a Server. modifyModel :: forall a e. Member (ModelState a) e => (Model a -> Model a) -> Eff e () -- | Modify the Model of a Server and return the old value. getAndModifyModel :: forall a e. Member (ModelState a) e => (Model a -> Model a) -> Eff e (Model a) -- | Modify the Model of a Server and return the new value. modifyAndGetModel :: forall a e. Member (ModelState a) e => (Model a -> Model a) -> Eff e (Model a) -- | Return the Model of a Server. getModel :: forall a e. Member (ModelState a) e => Eff e (Model a) -- | Overwrite the Model of a Server. putModel :: forall a e. Member (ModelState a) e => Model a -> Eff e () -- | Overwrite the Model of a Server, return the old value. getAndPutModel :: forall a e. Member (ModelState a) e => Model a -> Eff e (Model a) -- | Return a element selected by a Lens of the Model of a -- Server. useModel :: forall a b e. Member (ModelState a) e => Getting b (Model a) b -> Eff e b -- | Return a element selected by a Lens of the Model of a -- Server. preuseModel :: forall a b e. Member (ModelState a) e => Getting (First b) (Model a) b -> Eff e (Maybe b) -- | Run an action that modifies portions of the Model of a -- Server defined by the given Lens. zoomModel :: forall a b c e. Member (ModelState a) e => Lens' (Model a) b -> Eff (State b : e) c -> Eff e c -- | Log the Model of a Server using logDebug. logModel :: forall m e q. (Show (Model m), Member Logs e, HasProcesses e q, Member (ModelState m) e) => Text -> Eff e () -- | The Effect type of readonly Settings in a Server -- instance. type SettingsReader a = Reader (Settings a) -- | Return the read-only Settings of a Server askSettings :: forall a e. Member (SettingsReader a) e => Eff e (Settings a) -- | Return the read-only Settings of a Server as viewed -- through a Lens viewSettings :: forall a b e. Member (SettingsReader a) e => Getting b (Settings a) b -> Eff e b -- | Map ModelState and SettingsReader effects. Use this to -- embed update from another Server instance. mapEffects :: forall inner outer a e. (Settings outer -> Settings inner) -> Lens' (Model outer) (Model inner) -> Eff (ModelState inner : (SettingsReader inner : e)) a -> Eff (ModelState outer : (SettingsReader outer : e)) a -- | Coerce Coercible ModelState and SettingsReader -- effects. Use this to embed update from a similar -- Server instance. coerceEffects :: forall inner outer a e. (Coercible (Model inner) (Model outer), Coercible (Model outer) (Model inner), Coercible (Settings outer) (Settings inner)) => Eff (ModelState inner : (SettingsReader inner : e)) a -> Eff (ModelState outer : (SettingsReader outer : e)) a -- | This event sum-type is used to communicate incoming messages and other -- events to the instances of Server. data Event a -- | A Synchronous message was received. If an implementation wants -- to delegate nested Pdus, it can use -- toEmbeddedReplyTarget to convert a ReplyTarget safely to -- the embedded protocol. [OnCall] :: forall a r. (Tangible r, TangiblePdu a ( 'Synchronous r)) => ReplyTarget a r -> Pdu a ( 'Synchronous r) -> Event a [OnCast] :: forall a. TangiblePdu a 'Asynchronous => Pdu a 'Asynchronous -> Event a [OnInterrupt] :: Interrupt 'Recoverable -> Event a [OnDown] :: ProcessDown -> Event a [OnTimeOut] :: TimerElapsed -> Event a [OnMessage] :: StrictDynamic -> Event a instance Control.Eff.Concurrent.Protocol.StatefulServer.Server a q => Control.Eff.Concurrent.Protocol.EffectfulServer.Server (Control.Eff.Concurrent.Protocol.StatefulServer.Stateful a) q -- | Functions for protocol clients. -- -- This modules is required to write clients that sendPdus. module Control.Eff.Concurrent.Protocol.Client -- | Send a request Pdu that has no reply and return immediately. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasCallStack, HasProcesses r q, HasPdu destination, HasPdu protocol, Tangible (Pdu destination 'Asynchronous), Embeds destination protocol) => Endpoint destination -> Pdu protocol 'Asynchronous -> Eff r () -- | Send a request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu clause -- is Synchronous. -- -- Always prefer callWithTimeout over call call :: forall result destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Embeds destination protocol, HasCallStack) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Eff r result -- | Send an request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Member Logs r, HasCallStack, Embeds destination protocol) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Timeout -> Eff r result -- | Like castEndpointReader, but uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- castEndpointReader. castSingleton :: forall outer inner q e. (HasCallStack, Member (EndpointReader outer) e, Tangible (Pdu outer 'Asynchronous), HasProcesses e q, HasPdu outer, HasPdu inner, Embeds outer inner, Embeds outer outer) => Pdu inner 'Asynchronous -> Eff e () -- | Like cast but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use castSingleton. castEndpointReader :: forall o r q. (HasEndpointReader o r, HasProcesses r q, Tangible (Pdu o 'Asynchronous), HasCallStack, HasPdu o, Embeds o o) => Pdu o 'Asynchronous -> Eff r () -- | Like callEndpointReader, uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- callEndpointReader. callSingleton :: forall outer inner reply q e. (HasCallStack, Member (EndpointReader outer) e, Embeds outer inner, Embeds outer outer, HasProcesses e q, TangiblePdu outer ( 'Synchronous reply), TangiblePdu inner ( 'Synchronous reply), Tangible reply) => Pdu inner ( 'Synchronous reply) -> Eff e reply -- | Like call but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use callSingleton. callEndpointReader :: forall reply o r q. (HasEndpointReader o r, HasCallStack, Tangible reply, TangiblePdu o ( 'Synchronous reply), HasProcesses r q, Embeds o o) => Pdu o ( 'Synchronous reply) -> Eff r reply -- | Instead of passing around a Endpoint value and passing to -- functions like cast or call, a Endpoint can -- provided by a Reader effect, if there is only a single -- server for a given Pdu instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Endpoint. type HasEndpointReader o r = (Typeable o, Member (EndpointReader o) r) -- | The reader effect for ProcessIds for Pdus, see -- runEndpointReader type EndpointReader o = Reader (Endpoint o) -- | Get the Endpoint registered with runEndpointReader. askEndpoint :: Member (EndpointReader o) e => Eff e (Endpoint o) -- | Run a reader effect that contains the one server handling a -- specific Pdu instance. runEndpointReader :: HasCallStack => Endpoint o -> Eff (EndpointReader o : r) a -> Eff r a -- | Observer Effects -- -- This module supports the implementation of observerRegistry and -- observables. Expected use case is event propagation. -- -- The observable event sources and the observers are usually server -- processes for a protocol that embeds the ObserverRegistry and -- Observer Pdus respectively. -- -- A generic FIFO queue based observer can be found in -- Control.Eff.Concurrent.Protocol.Observer.Queue. module Control.Eff.Concurrent.Protocol.Observer -- | A protocol to communicate Observed events from a sources -- to many sinks. -- -- A sink is any process that serves a protocol with a Pdu -- instance that embeds the Observer Pdu via an HasPduPrism -- instance. -- -- This type has dual use, for one it serves as type-index for -- Pdu, i.e. HasPdu respectively, and secondly it contains -- an ObservationSink and a MonitorReference. -- -- The ObservationSink is used to serialize and send the -- Observed events, while the ProcessId serves as key for -- internal maps. newtype Observer event MkObserver :: Arg ProcessId (ObservationSink event) -> Observer event -- | The Information necessary to wrap an Observed event to a -- process specific message, e.g. the embedded Observer Pdu -- instance, and the MonitorReference of the destination process. data ObservationSink event -- | Convenience type alias. type IsObservable eventSource event = (Tangible event, Embeds eventSource (ObserverRegistry event), HasPdu eventSource) -- | Convenience type alias. type CanObserve eventSink event = (Tangible event, Embeds eventSink (Observer event), HasPdu eventSink) -- | The protocol data unit type for the given protocol. data family Pdu protocol (reply :: Synchronicity) -- | And an Observer to the set of recipients for all observations -- reported by observerRegistryNotify. Note that the -- observerRegistry 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 :: forall event eventSink eventSource r q. (HasCallStack, HasProcesses r q, IsObservable eventSource event, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message forgetObserver :: forall event eventSink eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), IsObservable eventSource event, CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message, use a raw ProcessId as -- parameter. forgetObserverUnsafe :: forall event eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), IsObservable eventSource event) => Endpoint eventSource -> ProcessId -> Eff r () -- | A protocol for managing Observers, encompassing registration -- and de-registration of Observers. data ObserverRegistry (event :: Type) MkObserverRegistry :: Map ProcessId (ObservationSink event) -> ObserverRegistry [_observerRegistry] :: ObserverRegistry -> Map ProcessId (ObservationSink event) -- | Alias for the effect that contains the observers managed by -- evalObserverRegistryState type ObserverRegistryState event = State (ObserverRegistry event) -- | Report an observation to all observers. The process needs to -- evalObserverRegistryState and to -- observerRegistryHandlePdu. observerRegistryNotify :: forall event r q. (HasProcesses r q, Member (ObserverRegistryState event) r, Tangible event, HasCallStack) => event -> Eff r () -- | Keep track of registered Observers. -- -- Handle the ObserverRegistryState effect, i.e. run -- evalState on an emptyObserverRegistry. evalObserverRegistryState :: HasCallStack => Eff (ObserverRegistryState event : r) a -> Eff r a -- | The empty ObserverRegistryState emptyObserverRegistry :: ObserverRegistry event -- | Provide the implementation for the ObserverRegistry Protocol, -- this handled RegisterObserver and ForgetObserver -- messages. It also adds the ObserverRegistryState constraint to -- the effect list. observerRegistryHandlePdu :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => Pdu (ObserverRegistry event) 'Asynchronous -> Eff r () -- | Remove the entry in the ObserverRegistry for the -- ProcessId and return True if there was an entry, -- False otherwise. observerRegistryRemoveProcess :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => ProcessId -> Eff r Bool instance forall k (event :: k). GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Observer.Observer event) instance forall k (event :: k). GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Observer.Observer event) instance forall k (event :: k). GHC.Generics.Generic (Control.Eff.Concurrent.Protocol.Observer.ObservationSink event) instance Control.Eff.Concurrent.Protocol.Tangible event => Control.Eff.Concurrent.Protocol.HasPdu (Control.Eff.Concurrent.Protocol.Observer.ObserverRegistry event) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Observer.ObserverRegistry event) r) instance Data.Typeable.Internal.Typeable event => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Observer.ObserverRegistry event) r) instance forall k (event :: k). Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Observer.Observer event) instance forall k (event :: k). Data.Typeable.Internal.Typeable event => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Observer.Observer event) instance Control.Eff.Concurrent.Protocol.Tangible event => Control.Eff.Concurrent.Protocol.HasPdu (Control.Eff.Concurrent.Protocol.Observer.Observer event) instance Control.DeepSeq.NFData event => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Observer.Observer event) r) instance GHC.Show.Show event => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Observer.Observer event) r) instance forall k (event :: k). Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Observer.ObservationSink event) -- | A small process to capture and _share_ observation's by enqueueing -- them into an STM TBQueue. module Control.Eff.Concurrent.Protocol.Observer.Queue -- | Contains a TBQueue capturing observations. See observe. newtype ObservationQueue a ObservationQueue :: TBQueue a -> ObservationQueue a -- | A Reader for an ObservationQueue. type Reader a = Reader (ObservationQueue a) -- | Listen to, and capture observations in an ObservationQueue. -- -- Fork an Observer process that runs only while the body -- expression is executed. Register the observer to the observable -- process passed to this function. -- -- The captured observations can be obtained by await, -- tryRead and flush. -- -- The queue size is limited to the given number. -- --

Example

-- --
--   import qualified Control.Eff.Concurrent.Observer.Queue as OQ
--   
--   foo =
--     do
--       observed <- startLink SomeObservable
--       OQ.observe 100 observed $ do
--         ...
--         cast observed DoSomething
--         evt <- OQ.await @TestEvent
--         ...
--   
observe :: forall event eventSource e q len b. (HasCallStack, HasProcesses e q, FilteredLogging e, FilteredLogging q, FilteredLogging (Processes q), Lifted IO e, Lifted IO q, IsObservable eventSource event, Integral len, Server (ObservationQueue event) (Processes q), Tangible (Pdu eventSource 'Asynchronous)) => len -> Endpoint eventSource -> Eff (Reader event : e) b -> Eff e b -- | Read queued observations captured and enqueued in the shared -- ObservationQueue by observe. -- -- This blocks until something was captured or an interrupt or exceptions -- was thrown. For a non-blocking variant use tryRead or -- flush. await :: forall event r. (Member (Reader event) r, HasCallStack, MonadIO (Eff r), Typeable event, Member Logs r) => Eff r event -- | Read queued observations captured and enqueued in the shared -- ObservationQueue by observe. -- -- Return the oldest enqueued observation immediately or Nothing -- if the queue is empty. Use await to block until an observation -- is observerRegistryNotify. tryRead :: forall event r. (Member (Reader event) r, HasCallStack, MonadIO (Eff r), Typeable event, Member Logs r) => Eff r (Maybe event) -- | Read at once all currently queued observations captured and enqueued -- in the shared ObservationQueue by observe. -- -- This returns immediately all currently enqueued observations. For a -- blocking variant use await. flush :: forall event r. (Member (Reader event) r, HasCallStack, MonadIO (Eff r), Typeable event, Member Logs r) => Eff r [event] instance Data.Default.Class.Default (Control.Eff.Concurrent.Protocol.StatefulServer.Model (Control.Eff.Concurrent.Protocol.Observer.Queue.ObservationQueue event)) instance (Data.Typeable.Internal.Typeable event, Control.Eff.Internal.Lifted GHC.Types.IO q, Data.OpenUnion.Member Control.Eff.Log.Handler.Logs q) => Control.Eff.Concurrent.Protocol.StatefulServer.Server (Control.Eff.Concurrent.Protocol.Observer.Queue.ObservationQueue event) (Control.Eff.Concurrent.Process.Processes q) -- | 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 (Processes 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 (Processes r) a -> IO a -- | Combination of submit and cast. submitCast :: forall o r. (SetMember Lift (Lift IO) r, HasPdu o, Tangible (Pdu o 'Asynchronous), Member Interrupts r) => SchedulerSession r -> Endpoint o -> Pdu o 'Asynchronous -> IO () -- | Combination of submit and cast. submitCall :: forall o q r. (SetMember Lift (Lift IO) r, Member Interrupts r, Tangible (Pdu o ( 'Synchronous q)), HasPdu o, Tangible q) => SchedulerSession r -> Endpoint o -> Pdu o ( 'Synchronous q) -> IO q -- | Build a Control.Eff.Concurrent.EffectfulServer from callbacks. -- -- This module contains in instance of Server that delegates to -- callback functions. module Control.Eff.Concurrent.Protocol.CallbackServer -- | Execute the server loop, that dispatches incoming events to either a -- set of Callbacks or CallbacksEff. start :: forall (tag :: Type) eLoop q e. (HasCallStack, TangibleCallbacks tag eLoop q, Server (Server tag eLoop q) (Processes q), FilteredLogging (Processes q), HasProcesses e q) => CallbacksEff tag eLoop q -> Eff e (Endpoint tag) -- | Execute the server loop, that dispatches incoming events to either a -- set of Callbacks or CallbacksEff. startLink :: forall (tag :: Type) eLoop q e. (HasCallStack, TangibleCallbacks tag eLoop q, Server (Server tag eLoop q) (Processes q), FilteredLogging (Processes q), HasProcesses e q) => CallbacksEff tag eLoop q -> Eff e (Endpoint tag) -- | Phantom type to indicate a callback based Server instance. data Server tag eLoop e -- | The name/id of a Server for logging purposes. newtype ServerId (tag :: Type) MkServerId :: Text -> ServerId [_fromServerId] :: ServerId -> Text -- | This event sum-type is used to communicate incoming messages and other -- events to the instances of Server. data Event a -- | A Synchronous message was received. If an implementation wants -- to delegate nested Pdus, it can use -- toEmbeddedReplyTarget to convert a ReplyTarget safely to -- the embedded protocol. [OnCall] :: forall a r. (Tangible r, TangiblePdu a ( 'Synchronous r)) => ReplyTarget a r -> Pdu a ( 'Synchronous r) -> Event a [OnCast] :: forall a. TangiblePdu a 'Asynchronous => Pdu a 'Asynchronous -> Event a [OnInterrupt] :: Interrupt 'Recoverable -> Event a [OnDown] :: ProcessDown -> Event a [OnTimeOut] :: TimerElapsed -> Event a [OnMessage] :: StrictDynamic -> Event a -- | The constraints for a tangible Server instance. type TangibleCallbacks tag eLoop e = (HasProcesses eLoop e, Typeable e, Typeable eLoop, Typeable tag) -- | A convenience type alias for callbacks that do not need a custom -- effect. type Callbacks tag e = CallbacksEff tag (Processes e) e -- | A smart constructor for Callbacks. callbacks :: forall tag q. (HasCallStack, TangibleCallbacks tag (Processes q) q, Server (Server tag (Processes q) q) (Processes q), FilteredLogging q) => (Endpoint tag -> Event tag -> Eff (Processes q) ()) -> ServerId tag -> Callbacks tag q -- | A simple smart constructor for Callbacks. onEvent :: forall tag q. (HasCallStack, TangibleCallbacks tag (Processes q) q, Server (Server tag (Processes q) q) (Processes q), FilteredLogging q) => (Event tag -> Eff (Processes q) ()) -> ServerId (tag :: Type) -> Callbacks tag q -- | A convenience type alias for effectful callback based -- Server instances. -- -- See Callbacks. type CallbacksEff tag eLoop e = Init (Server tag eLoop e) -- | A smart constructor for CallbacksEff. callbacksEff :: forall tag eLoop q. (HasCallStack, TangibleCallbacks tag eLoop q, Server (Server tag eLoop q) (Processes q), FilteredLogging q) => (forall x. Endpoint tag -> Eff eLoop x -> Eff (Processes q) x) -> (Endpoint tag -> Event tag -> Eff eLoop ()) -> ServerId tag -> CallbacksEff tag eLoop q -- | A simple smart constructor for CallbacksEff. onEventEff :: (HasCallStack, TangibleCallbacks tag eLoop q, Server (Server tag eLoop q) (Processes q), FilteredLogging q) => (forall a. Eff eLoop a -> Eff (Processes q) a) -> (Event tag -> Eff eLoop ()) -> ServerId (tag :: Type) -> CallbacksEff tag eLoop q instance Data.String.IsString (Control.Eff.Concurrent.Protocol.CallbackServer.ServerId tag) instance GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.CallbackServer.ServerId tag) instance GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.CallbackServer.ServerId tag) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.CallbackServer.ServerId tag) instance Data.Typeable.Internal.Typeable tag => GHC.Show.Show (Control.Eff.Concurrent.Protocol.CallbackServer.ServerId tag) instance Control.Eff.Concurrent.Protocol.CallbackServer.TangibleCallbacks tag eLoop e => Control.Eff.Concurrent.Protocol.EffectfulServer.Server (Control.Eff.Concurrent.Protocol.CallbackServer.Server tag eLoop e) (Control.Eff.Concurrent.Process.Processes e) instance Control.Eff.Concurrent.Protocol.CallbackServer.TangibleCallbacks tag eLoop e => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.EffectfulServer.Init (Control.Eff.Concurrent.Protocol.CallbackServer.Server tag eLoop e)) instance Control.Eff.Concurrent.Protocol.CallbackServer.TangibleCallbacks tag eLoop e => GHC.Show.Show (Control.Eff.Concurrent.Protocol.EffectfulServer.Init (Control.Eff.Concurrent.Protocol.CallbackServer.Server tag eLoop e)) -- | A process broker 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 broker. Children can be -- started, but not restarted. -- -- Children can efficiently be looked-up by an id-value, and when the -- broker is shutdown, all children will be shutdown these are actually -- all the features of this broker implementation. -- -- Also, this minimalist broker only knows how to spawn a single kind of -- child process. -- -- When a broker spawns a new child process, it expects the child process -- to return a ProcessId. The broker 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 broker process, and also, a child may -- break free from the broker by unlinking. -- -- Now while this seems nice at first, this might actually cause -- surprising results, since it is usually expected that stopping a -- broker also stops the children, or that a child exit shows up in the -- logging originating from the former broker. -- -- The approach here is to allow any child to link to the broker to -- realize when the broker was violently killed, and otherwise give the -- child no chance to unlink itself from its broker. -- -- This module is far simpler than the Erlang/OTP counter part, of a -- simple_one_for_one supervisor. -- -- The future of this broker might not be a-lot more than it currently -- is. The ability to restart processes might be implemented outside of -- this broker module. -- -- One way to do that is to implement the restart logic in a separate -- module, since the child-id can be reused when a child exits. module Control.Eff.Concurrent.Protocol.Broker -- | Start and link a new broker process with the given -- SpawnFununction. -- -- To spawn new child processes use spawnChild. startLink :: forall p e. (HasCallStack, IoLogging (Processes e), TangibleBroker p, Server (Broker p) (Processes e)) => StartArgument (Broker p) -> Eff (Processes e) (Endpoint (Broker p)) -- | A smart constructor for MkBrokerConfig that makes it easy to -- start a Server instance. -- -- The user needs to instantiate ChildId p. statefulChild :: forall p e. (HasCallStack, IoLogging e, TangibleBroker (Stateful p), Server (Broker (Stateful p)) e) => Timeout -> (ChildId p -> StartArgument p) -> StartArgument (Broker (Stateful p)) -- | Stop the broker and shutdown all processes. -- -- Block until the broker has finished. stopBroker :: (HasCallStack, HasProcesses e q, Member Logs e, Lifted IO e, TangibleBroker p) => Endpoint (Broker p) -> Eff e () -- | Check if a broker process is still alive. isBrokerAlive :: forall p q0 e. (HasCallStack, Member Logs e, Typeable p, HasProcesses e q0) => Endpoint (Broker p) -> Eff e Bool -- | Monitor a broker process. monitorBroker :: forall p q0 e. (HasCallStack, Member Logs e, HasProcesses e q0, TangibleBroker p) => Endpoint (Broker p) -> Eff e MonitorReference -- | Return a Text describing the current state of the broker. getDiagnosticInfo :: forall p e q0. (HasCallStack, HasProcesses e q0, TangibleBroker p) => Endpoint (Broker p) -> Eff e Text -- | Start and monitor a new child process using the SpawnFun -- passed to startLink. spawnChild :: forall p q0 e. (HasCallStack, Member Logs e, HasProcesses e q0, TangibleBroker p, Typeable (ServerPdu p)) => Endpoint (Broker p) -> ChildId p -> Eff e (Either (SpawnErr p) (Endpoint (ServerPdu p))) -- | Start and monitor a new child process using the SpawnFun -- passed to startLink. -- -- Call spawnChild and unpack the Either result, ignoring -- the AlreadyStarted error. spawnOrLookup :: forall p q0 e. (HasCallStack, Member Logs e, HasProcesses e q0, TangibleBroker p, Typeable (ServerPdu p)) => Endpoint (Broker p) -> ChildId p -> Eff e (Endpoint (ServerPdu p)) -- | Lookup the given child-id and return the output value of the -- SpawnFun if the client process exists. lookupChild :: forall p e q0. (HasCallStack, Member Logs e, HasProcesses e q0, TangibleBroker p, Typeable (ServerPdu p)) => Endpoint (Broker p) -> ChildId p -> Eff e (Maybe (Endpoint (ServerPdu p))) callById :: forall destination protocol result e q0. (HasCallStack, Member Logs e, HasProcesses e q0, Lifted IO e, Lifted IO q0, TangibleBroker protocol, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Embeds (ServerPdu destination) protocol, Ord (ChildId destination), Tangible (ChildId destination), Typeable (ServerPdu destination), Tangible result, NFData (Pdu protocol ( 'Synchronous result)), NFData (Pdu (ServerPdu destination) ( 'Synchronous result)), Show (Pdu (ServerPdu destination) ( 'Synchronous result))) => Endpoint (Broker destination) -> ChildId destination -> Pdu protocol ( 'Synchronous result) -> Timeout -> Eff e result castById :: forall destination protocol e q0. (HasCallStack, Member Logs e, HasProcesses e q0, TangibleBroker protocol, TangiblePdu destination 'Asynchronous, TangiblePdu protocol 'Asynchronous) => Endpoint (Broker destination) -> ChildId destination -> Pdu protocol 'Asynchronous -> Eff e () -- | 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 :: forall p e q0. (HasCallStack, Member Logs e, HasProcesses e q0, TangibleBroker p) => Endpoint (Broker p) -> ChildId p -> Eff e Bool data ChildNotFound child [ChildNotFound] :: ChildId child -> Endpoint (Broker child) -> ChildNotFound child -- | The index type of Server supervisors. -- -- A Broker p manages the life cycle of the processes, -- running the Server p methods of that specific type. -- -- The broker maps an identifier value of type ChildId p -- to an Endpoint p. data Broker (p :: Type) -- | The protocol data unit type for the given protocol. data family Pdu protocol (reply :: Synchronicity) -- | The type of value used to index running Server processes -- managed by a Broker. -- -- Note, that the type you provide must be Tangible. type family ChildId p -- | The value that defines what is required to initiate a Server -- loop. data family StartArgument a brokerConfigChildStopTimeout :: StartArgument (Broker p) -> Timeout -- | Runtime-Errors occurring when spawning child-processes. data SpawnErr p AlreadyStarted :: ChildId p -> Endpoint (ServerPdu p) -> SpawnErr p -- | The event type to indicate that a child was started or stopped. -- -- The need for this type originated for the watchdog functionality -- introduced in 0.30.0. The watch dog shall restart a crashed child, and -- in order to do so, it must somehow monitor the child. Since no order -- is specified in which processes get the ProcessDown events, a -- watchdog cannot monitor a child and restart it immediately, because it -- might have received the process down event before the broker. So -- instead the watchdog can simply use the broker events, and monitor -- only the broker process. data ChildEvent p [OnChildSpawned] :: Endpoint (Broker p) -> ChildId p -> Endpoint (ServerPdu p) -> ChildEvent p [OnChildDown] :: Endpoint (Broker p) -> ChildId p -> Endpoint (ServerPdu p) -> Interrupt 'NoRecovery -> ChildEvent p [OnBrokerShuttingDown] :: Endpoint (Broker p) -> ChildEvent p instance GHC.Generics.Generic (Control.Eff.Concurrent.Protocol.Broker.SpawnErr p) instance GHC.Generics.Generic (Control.Eff.Concurrent.Protocol.Broker.ChildEvent p) instance GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Broker.ChildId p) => GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Broker.SpawnErr p) instance GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Broker.ChildId p) => GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Broker.SpawnErr p) instance (Data.Typeable.Internal.Typeable (Control.Eff.Concurrent.Protocol.EffectfulServer.ServerPdu p), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildId p)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.SpawnErr p) instance Data.Typeable.Internal.Typeable p => Control.Eff.Concurrent.Protocol.HasPdu (Control.Eff.Concurrent.Protocol.Broker.Broker p) instance (Control.Eff.Log.Handler.IoLogging q, Control.Eff.Concurrent.Protocol.Broker.TangibleBroker p, Control.Eff.Concurrent.Protocol.Tangible (Control.Eff.Concurrent.Protocol.Broker.ChildId p), Data.Typeable.Internal.Typeable (Control.Eff.Concurrent.Protocol.EffectfulServer.ServerPdu p), Control.Eff.Concurrent.Protocol.EffectfulServer.Server p (Control.Eff.Concurrent.Process.Processes q), Control.Eff.Concurrent.Process.HasProcesses (Control.Eff.Concurrent.Protocol.EffectfulServer.ServerEffects p (Control.Eff.Concurrent.Process.Processes q)) q) => Control.Eff.Concurrent.Protocol.StatefulServer.Server (Control.Eff.Concurrent.Protocol.Broker.Broker p) (Control.Eff.Concurrent.Process.Processes q) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.ChildId p) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.SpawnErr p) instance (GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildId child), Data.Typeable.Internal.Typeable child) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildNotFound child) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.ChildId c) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.ChildNotFound c) instance Data.Typeable.Internal.Typeable p => Control.Eff.Concurrent.Protocol.HasPduPrism (Control.Eff.Concurrent.Protocol.Broker.Broker p) (Control.Eff.Concurrent.Protocol.Observer.ObserverRegistry (Control.Eff.Concurrent.Protocol.Broker.ChildEvent p)) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.ChildId p) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.ChildEvent p) instance (Data.Typeable.Internal.Typeable p, Data.Typeable.Internal.Typeable (Control.Eff.Concurrent.Protocol.EffectfulServer.ServerPdu p), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildId p)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildEvent p) instance (Data.Typeable.Internal.Typeable p, GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildId p)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Broker.Broker p) r) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.ChildId p) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Broker.Broker p) r) -- | Monitor a process and act when it is unresponsive. -- -- Behaviour of the watchdog: -- -- When a child crashes: * if the allowed maximum number crashes per time -- span has been reached for the process, ** cancel all other timers ** -- don't start the child again ** if this is a permanent watchdog -- crash the watchdog * otherwise ** tell the broker to start the child -- ** record a crash and start a timer to remove the record later ** -- monitor the child -- -- When a child crash timer elapses: * remove the crash record module Control.Eff.Concurrent.Protocol.Watchdog -- | Start and link a new watchdog process. -- -- The watchdog process will register itself to the ChildEvents -- and restart crashed children. startLink :: forall child e q. (HasCallStack, Typeable child, FilteredLogging (Processes q), Member Logs q, HasProcesses e q, Tangible (ChildId child), Ord (ChildId child), HasPdu (ServerPdu child), Lifted IO q) => CrashRate -> Eff e (Endpoint (Watchdog child)) -- | The phantom for watchdog processes, that watch the given type of -- servers -- -- This type is used for the Server and HasPdu instances. data Watchdog (child :: Type) -- | Restart children of the given broker. -- -- When the broker exits, ignore the children of that broker. attachTemporary :: forall child q e. (HasCallStack, FilteredLogging e, Typeable child, HasPdu (ServerPdu child), Tangible (ChildId child), Ord (ChildId child), HasProcesses e q) => Endpoint (Watchdog child) -> Endpoint (Broker child) -> Eff e () -- | Restart children of the given broker. -- -- When the broker exits, the watchdog process will exit, too. attachPermanent :: forall child q e. (HasCallStack, FilteredLogging e, Typeable child, HasPdu (ServerPdu child), Tangible (ChildId child), Ord (ChildId child), HasProcesses e q) => Endpoint (Watchdog child) -> Endpoint (Broker child) -> Eff e () -- | Return a list of CrashReports. -- -- Useful for diagnostics getCrashReports :: forall child q e. (HasCallStack, FilteredLogging e, Typeable child, HasPdu (ServerPdu child), Tangible (ChildId child), Ord (ChildId child), HasProcesses e q, Lifted IO q, Lifted IO e, Member Logs e) => Endpoint (Watchdog child) -> Eff e (Map (ChildId child) (ChildWatch child)) -- | The limit of crashes (see CrashCount) per time span (see -- CrashTimeSpan) that justifies restarting child processes. -- -- Used as parameter for startLink. -- -- Use crashesPerSeconds to construct a value. -- -- This governs how long the ExonerationTimer runs before cleaning -- up a CrashReport in a ChildWatch. data CrashRate CrashesPerSeconds :: CrashCount -> CrashTimeSpan -> CrashRate [_crashCount] :: CrashRate -> CrashCount [_crashTimeSpan] :: CrashRate -> CrashTimeSpan -- | A lens for _crashCount. crashCount :: Lens' CrashRate CrashCount -- | A lens for _crashTimeSpan. crashTimeSpan :: Lens' CrashRate CrashTimeSpan -- | A smart constructor for CrashRate. -- -- The first parameter is the number of crashes allowed per number of -- seconds (second parameter) until the watchdog should give up -- restarting a child. crashesPerSeconds :: CrashCount -> CrashTimeSpan -> CrashRate -- | Number of crashes in CrashRate. type CrashCount = Int -- | Time span in which crashes are counted in CrashRate. type CrashTimeSpan = Int -- | An internal data structure that keeps the CrashReports of a -- child of an attached Broker monitored by a Watchdog. -- -- See attachPermanent and attachTemporary, -- ExonerationTimer, CrashRate. data ChildWatch child MkChildWatch :: Endpoint (Broker child) -> Set (CrashReport (ChildId child)) -> ChildWatch child -- | The attached Broker that started the child [_parent] :: ChildWatch child -> Endpoint (Broker child) -- | The crashes of the child. If the number of crashes surpasses the -- allowed number of crashes before the ExonerationTimers clean -- them, the child is finally crashed. [_crashes] :: ChildWatch child -> Set (CrashReport (ChildId child)) -- | A lens for _parent. parent :: Lens' (ChildWatch child) (Endpoint (Broker child)) -- | A lens for _crashes crashes :: Lens' (ChildWatch child) (Set (CrashReport (ChildId child))) -- | The timer started based on the CrashRate _crashTimeSpan -- when a CrashReport is recorded. -- -- After this timer elapses, the Watchdog server will remove the -- CrashReport from the ChildWatch of that child. data ExonerationTimer a MkExonerationTimer :: !a -> !TimerReference -> ExonerationTimer a -- | An internal data structure that records a single crash of a child of -- an attached Broker. -- -- See attachPermanent and attachTemporary. data CrashReport a MkCrashReport :: TimerReference -> UTCTime -> Interrupt 'NoRecovery -> CrashReport a -- | After a crash, an ExonerationTimer according to the -- CrashRate of the Watchdog is started, this is the -- reference [_exonerationTimerReference] :: CrashReport a -> TimerReference -- | Recorded time of the crash [_crashTime] :: CrashReport a -> UTCTime -- | Recorded crash reason [_crashReason] :: CrashReport a -> Interrupt 'NoRecovery -- | Lens for _crashTime crashTime :: Lens' (CrashReport a) UTCTime -- | Lens for _crashReason crashReason :: Lens' (CrashReport a) (Interrupt 'NoRecovery) -- | Lens for _exonerationTimerReference exonerationTimerReference :: Lens' (CrashReport a) TimerReference instance GHC.Classes.Ord a => GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Watchdog.ExonerationTimer a) instance GHC.Classes.Eq a => GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Watchdog.ExonerationTimer a) instance forall k (a :: k). GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Watchdog.CrashReport a) instance forall k (a :: k). GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Watchdog.CrashReport a) instance GHC.Classes.Ord Control.Eff.Concurrent.Protocol.Watchdog.CrashRate instance GHC.Classes.Eq Control.Eff.Concurrent.Protocol.Watchdog.CrashRate instance Data.Typeable.Internal.Typeable child => Control.Eff.Concurrent.Protocol.HasPdu (Control.Eff.Concurrent.Protocol.Watchdog.Watchdog child) instance (Data.Typeable.Internal.Typeable child, Control.Eff.Concurrent.Protocol.HasPdu (Control.Eff.Concurrent.Protocol.EffectfulServer.ServerPdu child), Control.Eff.Concurrent.Protocol.Tangible (Control.Eff.Concurrent.Protocol.Broker.ChildId child), GHC.Classes.Ord (Control.Eff.Concurrent.Protocol.Broker.ChildId child), GHC.Classes.Eq (Control.Eff.Concurrent.Protocol.Broker.ChildId child), Control.Eff.Internal.Lifted GHC.Types.IO e, Data.OpenUnion.Member Control.Eff.Log.Handler.Logs e) => Control.Eff.Concurrent.Protocol.StatefulServer.Server (Control.Eff.Concurrent.Protocol.Watchdog.Watchdog child) (Control.Eff.Concurrent.Process.Processes e) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Watchdog.ChildWatch child) instance (Data.Typeable.Internal.Typeable child, Data.Typeable.Internal.Typeable (Control.Eff.Concurrent.Protocol.Broker.ChildId child), GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildId child)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Watchdog.ChildWatch child) instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Watchdog.ExonerationTimer a) instance GHC.Show.Show a => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Watchdog.ExonerationTimer a) instance (GHC.Show.Show a, Data.Typeable.Internal.Typeable a) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Watchdog.CrashReport a) instance forall k (a :: k). Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Watchdog.CrashReport a) instance Data.Default.Class.Default Control.Eff.Concurrent.Protocol.Watchdog.CrashRate instance GHC.Show.Show Control.Eff.Concurrent.Protocol.Watchdog.CrashRate instance Control.DeepSeq.NFData Control.Eff.Concurrent.Protocol.Watchdog.CrashRate instance GHC.Show.Show Control.Eff.Concurrent.Protocol.Watchdog.BrokerWatch instance Data.Typeable.Internal.Typeable child => Control.Eff.Concurrent.Protocol.HasPduPrism (Control.Eff.Concurrent.Protocol.Watchdog.Watchdog child) (Control.Eff.Concurrent.Protocol.Observer.Observer (Control.Eff.Concurrent.Protocol.Broker.ChildEvent child)) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Broker.ChildId child) => Control.DeepSeq.NFData (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Watchdog.Watchdog child) r) instance (GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildId child), Data.Typeable.Internal.Typeable child, Data.Typeable.Internal.Typeable (Control.Eff.Concurrent.Protocol.EffectfulServer.ServerPdu child)) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.Pdu (Control.Eff.Concurrent.Protocol.Watchdog.Watchdog child) r) instance Data.Default.Class.Default (Control.Eff.Concurrent.Protocol.StatefulServer.Model (Control.Eff.Concurrent.Protocol.Watchdog.Watchdog child)) instance (GHC.Show.Show (Control.Eff.Concurrent.Protocol.Broker.ChildId child), Data.Typeable.Internal.Typeable (Control.Eff.Concurrent.Protocol.Broker.ChildId child), Data.Typeable.Internal.Typeable child) => GHC.Show.Show (Control.Eff.Concurrent.Protocol.StatefulServer.Model (Control.Eff.Concurrent.Protocol.Watchdog.Watchdog child)) -- | Enrich LogMessages with timestamps and OS dependent -- information. module Control.Eff.LogWriter.Rich -- | Decorate an IO based LogWriter to fill out these fields in -- LogMessages: -- -- -- -- It works by using mappingLogWriterIO. richLogWriter :: Text -> Facility -> LogWriter -> LogWriter -- | Enable logging to IO using the richLogWriter. -- -- Example: -- --
--   exampleWithIoLogging :: IO ()
--   exampleWithIoLogging =
--       runLift
--     $ withRichLogging debugTraceLogWriter
--                     "my-app"
--                     local7
--                     (lmSeverityIsAtLeast informationalSeverity)
--     $ logInfo "Oh, hi there"
--   
withRichLogging :: Lifted IO e => LogWriter -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Render a LogMessage to stdout. -- -- This function will also set the BufferMode of stdout to -- LineBuffering. -- -- See ioHandleLogWriter. stdoutLogWriter :: LogMessageRenderer Text -> IO LogWriter -- | 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 withRichLogging. -- -- 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 renderLogMessageConsoleLog
--     $ logInfo "Oh, hi there"
--   
-- -- To vary the LogWriter use withRichLogging. withFileLogging :: (Lifted IO e, MonadBaseControl IO (Eff e), HasCallStack) => FilePath -> Text -> Facility -> LogPredicate -> LogMessageTextRenderer -> Eff (Logs : (LogWriterReader : 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
--     $ withoutLogging
--     $ withFileLogWriter "test.log" renderLogMessageConsoleLog
--     $ logInfo "Oh, hi there"
--   
withFileLogWriter :: (IoLogging e, MonadBaseControl IO (Eff e), HasCallStack) => FilePath -> LogMessageTextRenderer -> 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
--     $ withoutLogging @IO
--     $ withTraceLogWriter
--     $ logInfo "Oh, hi there"
--   
withTraceLogWriter :: IoLogging e => Eff e a -> Eff e a -- | Enable logging via traceM using the debugTraceLogWriter, -- with some LogMessage fields preset as in -- withRichLogging. -- -- 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 : e)) a -> Eff e a -- | Write LogMessages via traceM. debugTraceLogWriter :: LogMessageTextRenderer -> LogWriter -- | 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
--     $ withoutLogging @IO
--     $ withConsoleLogWriter
--     $ logInfo "Oh, hi there"
--   
withConsoleLogWriter :: IoLogging e => Eff e a -> Eff e a -- | Enable logging to standard output using the -- consoleLogWriter, with some LogMessage fields preset as -- in withRichLogging. -- -- 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 withRichLogging. withConsoleLogging :: Lifted IO e => Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Render a LogMessage to stdout using -- renderLogMessageConsoleLog. -- -- See stdoutLogWriter. consoleLogWriter :: IO LogWriter -- | 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 (Processes 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 (Processes 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 -> Eff EffectsIo a -> IO (Either (Interrupt 'NoRecovery) a) -- | Like scheduleIO but pure. The yield effect is -- just return (). schedulePure == runIdentity . -- scheduleM (Identity . run) (return ()) schedulePure :: Eff (Processes PureBaseEffects) a -> Either (Interrupt 'NoRecovery) a -- | The effect list for Process effects in the single threaded pure -- scheduler. -- -- See PureBaseEffects and Processes type PureEffects = Processes PureBaseEffects -- | The effect list for Process effects in the single threaded pure -- scheduler. This is like SafeProcesses, no Interrupts are -- present. -- -- See PureBaseEffects and SafeProcesses type PureSafeEffects = SafeProcesses PureBaseEffects -- | The effect list for a pure, single threaded scheduler contains only -- Logs and the LogWriterReader for PureLogWriter. type PureBaseEffects = '[Logs, LogWriterReader] -- | Constraint for the existence of the underlying scheduler effects. -- -- See PureBaseEffects type HasPureBaseEffects e = (HasCallStack, PureBaseEffects <:: e) -- | Execute a Process using scheduleM on top of Lift -- IO. All logging is written to the console using -- consoleLogWriter. -- -- To use another LogWriter use defaultMainWithLogWriter -- instead. defaultMain :: HasCallStack => Eff EffectsIo () -> IO () -- | Execute a Process using scheduleM on top of Lift -- IO. All logging is written using the given LogWriter. defaultMainWithLogWriter :: HasCallStack => LogWriter -> Eff EffectsIo () -> IO () -- | 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 (Processes r) a -> m (Either (Interrupt 'NoRecovery) a) -- | The effect list for Process effects in the single threaded -- scheduler. -- -- See BaseEffectsIo type EffectsIo = Processes BaseEffectsIo -- | The effect list for Process effects in the single threaded -- scheduler. This is like SafeProcesses, no Interrupts are -- present. -- -- See BaseEffectsIo. type SafeEffectsIo = SafeProcesses BaseEffectsIo -- | The effect list for the underlying scheduler. -- -- See LoggingAndIo type BaseEffectsIo = LoggingAndIo -- | Constraint for the existence of the underlying scheduler effects. type HasBaseEffectsIo e = (HasCallStack, Lifted IO e, LoggingAndIo <:: e) instance GHC.Show.Show (Control.Eff.Concurrent.Process.SingleThreadedScheduler.OnYield r a) instance GHC.Show.Show (Control.Eff.Concurrent.Process.SingleThreadedScheduler.STS r m) instance GHC.Show.Show Control.Eff.Concurrent.Process.SingleThreadedScheduler.ProcessInfo -- | 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 :: (IoLogging e, MonadBaseControl IO (Eff e), Integral len) => len -> Eff e a -> Eff e a -- | This is a wrapper around withAsyncLogWriter and -- withRichLogging. -- -- 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 -> len -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | 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 -- BaseEffects effect for concurrent logging. schedule :: HasCallStack => Eff Effects () -> Eff LoggingAndIo () -- | Start the message passing concurrency system then execute a -- Process on top of BaseEffects effect. All logging is -- sent to standard output. defaultMain :: HasCallStack => Eff Effects () -> IO () -- | Start the message passing concurrency system then execute a -- Process on top of BaseEffects effect. All logging is -- sent to standard output. defaultMainWithLogWriter :: HasCallStack => LogWriter -> Eff Effects () -> IO () -- | The concrete list of Effects of processes compatible with this -- scheduler. This builds upon BaseEffects. type SafeEffects = SafeProcesses BaseEffects -- | The Effects for interruptable, concurrent processes, scheduled -- via forkIO. type Effects = Processes BaseEffects -- | The concrete list of Effects for this scheduler implementation. type BaseEffects = 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 HasBaseEffects r = (HasCallStack, Lifted IO r, BaseEffects <:: 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 :: (IoLogging 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 withRichLogging. -- -- See exampleUdpRFC3164Logging withUDPLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => (LogMessage -> Text) -> String -> String -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : 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 :: (IoLogging 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 withRichLogging. -- -- See exampleDevLogSyslogLogging withUnixSocketLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => LogMessageRenderer Text -> FilePath -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Examples for FilteredLogging. 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: setLogWriter -- -- Also used: * stdoutLogWriter * -- renderConsoleMinimalisticWide * consoleLogWriter * -- logAlert * withLogging exampleSetLogWriter :: HasCallStack => IO () -- | Example code for: -- -- exampleLogTrace :: 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, IoLogging e) => Eff e () -- | Example logging client code using many LogPredicates. -- -- logPredicatesExampleClient :: (HasCallStack, IoLogging 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. -- -- This module re-exports most of the library. -- -- There are several scheduler implementations to choose from. -- -- This module re-exports -- Control.Eff.Concurrent.Process.ForkIOScheduler. -- -- To use another scheduler implementation, don't import this module, but -- instead import one of: -- -- 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 -> !Interrupt 'NoRecovery -> !ProcessId -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !Interrupt 'NoRecovery [downProcess] :: ProcessDown -> !ProcessId -- | 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 -- | Exceptions containing Interrupts. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc (Interrupt 'Recoverable) -- | A constraint for an effect set that requires the presence of -- SafeProcesses. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Process [e(N+1) .. e(N+k)], e(N+1) .. e(N+k)] -- -- It constrains e to support the (only) Process effect. -- -- This is more relaxed that HasProcesses since it does not -- require Interrupts. type HasSafeProcesses e inner = (SetMember Process (Process inner) e) -- | Cons Process onto a list of effects. This is called -- SafeProcesses because the the actions cannot be interrupted -- in. type SafeProcesses r = Process r : r -- | A constraint for an effect set that requires the presence of -- Processes. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Interrupts, Process [e(N+1) .. e(N+k)], e(N+1) .. -- e(N+k)] -- -- It constrains e beyond HasSafeProcesses to encompass -- Interrupts. type HasProcesses e inner = (HasSafeProcesses e inner, Member Interrupts e) -- | This adds a layer of the Interrupts effect on top of -- Processes type Processes e = Interrupts : SafeProcesses e -- | 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 -- | Extension of ExitNormally with a custom reason [NormalExitRequestedWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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, see Link for a discussion on linking. [LinkedProcessCrashed] :: ProcessId -> Interrupt 'Recoverable -- | An exit reason that has an error message and is Recoverable. [ErrorInterrupt] :: String -> Interrupt 'Recoverable -- | An interrupt with a custom message. [InterruptedBy] :: forall a. (Typeable a, Show a, NFData a) => a -> Interrupt 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: Interrupt 'NoRecovery -- | A process function returned or exited without any error, and with a -- custom message [ExitNormallyWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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 -- recovered. [ExitUnhandledError] :: Text -> Interrupt 'NoRecovery -- | A process shall exit immediately, without any cleanup was cancelled -- (e.g. killed, in cancel) [ExitProcessCancelled] :: Maybe ProcessId -> Interrupt 'NoRecovery -- | A process that is vital to the crashed process was not running [ExitOtherProcessNotRunning] :: ProcessId -> 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 -- | 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 -- | Serialize a message into a StrictDynamic value to be -- sent via sendAnyMessage. -- -- This indirection allows, among other things, the composition of -- Servers. newtype Serializer message MkSerializer :: (message -> StrictDynamic) -> Serializer message [runSerializer] :: Serializer message -> message -> StrictDynamic -- | 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 -- | A number of micro seconds. newtype Timeout TimeoutMicros :: Int -> Timeout [fromTimeoutMicros] :: Timeout -> Int -- | A multi-line text describing the current state of a process for -- debugging purposes. newtype ProcessDetails MkProcessDetails :: Text -> ProcessDetails [_fromProcessDetails] :: ProcessDetails -> Text -- | A short title for a Process for logging purposes. newtype ProcessTitle MkProcessTitle :: Text -> ProcessTitle [_fromProcessTitle] :: ProcessTitle -> Text -- | 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 ()) -- | Simply wait until the time in the given Timeout has elapsed and -- return. [Delay] :: Timeout -> 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] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | 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. -- -- You might wonder: Why not tearing down the linked process when exiting -- normally? I thought about this. If a process exits normally, it should -- have the opportunity to shutdown stuff explicitly. And if you want to -- make sure that there are no dangling child processes after e.g. a -- broker crash, you can always use monitor. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Update the ProcessDetails of a process [UpdateProcessDetails] :: ProcessDetails -> Process r (ResumeProcess ()) -- | Get the ProcessState (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe (ProcessTitle, ProcessDetails, ProcessState))) -- | An isomorphism lens for the ProcessTitle fromProcessTitle :: Lens' ProcessTitle Text -- | An isomorphism lens for the ProcessDetails fromProcessDetails :: Lens' ProcessDetails Text -- | 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 Processes by wrapping them -- up in interruptToExit and then do a process Shutdown. provideInterruptsShutdown :: forall e a. Eff (Processes e) a -> Eff (SafeProcesses 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, HasProcesses r q) => 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasProcesses r q, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Use executeAndResumeOrExit to execute YieldProcess. -- Refer to YieldProcess for more information. yieldProcess :: forall r q. (HasProcesses r q, HasCallStack) => Eff r () -- | Simply block until the time in the Timeout has passed. delay :: forall r q. (HasProcesses r q, HasCallStack) => Timeout -> 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 o r q. (HasProcesses r q, HasCallStack, 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. (HasCallStack, HasProcesses r q) => 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. (HasCallStack, HasProcesses r q) => 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. (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r () -- | Start a new process, and immediately link to it. -- -- See Link for a discussion on linking. spawnLink :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes 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 SafeProcesses effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff r () -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r Bool -- | Return the ProcessTitle, ProcessDetails and -- ProcessState, for the given process, if the process is alive. getProcessState :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r (Maybe (ProcessTitle, ProcessDetails, ProcessState)) -- | Replace the ProcessDetails of the process. updateProcessDetails :: forall r q. (HasCallStack, HasProcesses r q) => ProcessDetails -> Eff r () -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => 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, HasProcesses r q) => Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, HasProcesses r q) => 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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, HasSafeProcesses r q) => Eff r ProcessId -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, HasProcesses r q) => MonitorReference -> Eff r () -- | monitor another process before while performing an action and -- demonitor afterwards. withMonitor :: (HasCallStack, HasProcesses r q) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, HasProcesses r q, 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. -- -- The parameter is the value obtained by monitor. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | A MessageSelector for the ProcessDown message. of a -- specific process. -- -- In contrast to selectProcessDown this function matches the -- ProcessId. selectProcessDownByProcessId :: ProcessId -> 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. -- -- See Link for a discussion on linking. linkProcess :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. unlinkProcess :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r () -- | Exit the process with a Interrupt. exitBecause :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Interrupt 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, HasSafeProcesses r q) => String -> Eff r a -- | A ProcessId and a Serializer. EXPERIMENTAL -- -- See sendToReceiver. data Receiver a Receiver :: ProcessId -> (a -> out) -> Receiver a [_receiverPid] :: Receiver a -> ProcessId [_receiverSerializer] :: Receiver a -> a -> out fromProcessId :: Iso' ProcessId Int -- | Serialize and send a message to the process in a Receiver. -- -- EXPERIMENTAL sendToReceiver :: (NFData o, HasProcesses r q) => Receiver o -> o -> Eff r () receiverPid :: forall a_aHaF. Lens' (Receiver a_aHaF) ProcessId -- | The concrete list of Effects for this scheduler implementation. type BaseEffects = 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 HasBaseEffects r = (HasCallStack, Lifted IO r, BaseEffects <:: r) -- | The Effects for interruptable, concurrent processes, scheduled -- via forkIO. type Effects = Processes BaseEffects -- | The concrete list of Effects of processes compatible with this -- scheduler. This builds upon BaseEffects. type SafeEffects = SafeProcesses BaseEffects -- | Start the message passing concurrency system then execute a -- Process on top of BaseEffects effect. All logging is -- sent to standard output. defaultMain :: HasCallStack => Eff Effects () -> IO () -- | Start the message passing concurrency system then execute a -- Process on top of BaseEffects effect. All logging is -- sent to standard output. defaultMainWithLogWriter :: HasCallStack => LogWriter -> Eff Effects () -> IO () -- | This is the main entry point to running a message passing concurrency -- application. This function takes a Process on top of the -- BaseEffects effect for concurrent logging. schedule :: HasCallStack => Eff Effects () -> Eff LoggingAndIo () -- | 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 -- | 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. (HasCallStack, HasProcesses r q, 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. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitor combined with -- receiveSelectedAfter. receiveSelectedWithMonitorAfter :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> Eff r (Either (Either ProcessDown TimerElapsed) a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfterWithTitle. receiveAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Typeable a, NFData a, Show a) => Timeout -> ProcessTitle -> 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 startTimerWithTitle. receiveSelectedAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> ProcessTitle -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitorWithTitle combined with -- receiveSelectedAfterWithTitle. receiveSelectedWithMonitorAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> ProcessTitle -> 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. (HasCallStack, HasProcesses r q, Typeable message, NFData message) => ProcessId -> Timeout -> (TimerReference -> message) -> Eff r TimerReference -- | Like sendAfter but with a user provided name for the timer -- process. sendAfterWithTitle :: forall r q message. (HasCallStack, HasProcesses r q, Typeable message, NFData message) => ProcessTitle -> 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. -- -- This calls sendAfterWithTitle under the hood with -- TimerElapsed as message. startTimerWithTitle :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Timeout -> 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. -- -- Calls sendAfter under the hood. startTimer :: forall r q. (HasCallStack, HasProcesses r q) => Timeout -> Eff r TimerReference -- | Cancel a timer started with startTimer. cancelTimer :: forall r q. (HasCallStack, HasProcesses r q) => TimerReference -> Eff r () -- | A class for Pdu instances that embed other Pdu. -- -- This is a part of Embeds provide instances for your Pdus -- but in client code use the Embeds constraint. -- -- Instances of this class serve as proof to Embeds that a -- conversion into another Pdu actually exists. -- -- A Prism for the embedded Pdu is the center of this class -- -- Laws: embeddedPdu = prism' embedPdu fromPdu class (Typeable protocol, Typeable embeddedProtocol) => HasPduPrism protocol embeddedProtocol -- | A Prism for the embedded Pdus. embeddedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Prism' (Pdu protocol result) (Pdu embeddedProtocol result) -- | Embed the Pdu value of an embedded protocol into the -- corresponding Pdu value. embedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu embeddedProtocol result -> Pdu protocol result -- | Examine a Pdu value from the outer protocol, and return it, if -- it embeds a Pdu of embedded protocol, otherwise return -- Nothing/ fromPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu protocol result -> Maybe (Pdu embeddedProtocol result) -- | This type function takes an Pdu and analysis the reply type, -- i.e. the Synchronicity and evaluates to either t for -- an Pdu x (Synchronous t) or to '()' for an Pdu x -- Asynchronous. type family ProtocolReply (s :: Synchronicity) -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Pdu -- 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 Constraint that bundles the requirements for the Pdu -- values of a protocol. -- -- This ensures that Pdus can be strictly and deeply evaluated and -- shown such that for example logging is possible. type TangiblePdu p r = (Typeable p, Typeable r, Tangible (Pdu p r), HasPdu p) -- | 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. type Tangible i = (NFData i, Typeable i, Show i) -- | A constraint that requires that the outer Pdu has a -- clause to embed values from the inner Pdu. -- -- Also, this constraint requires a HasPduPrism instance, as a -- proof for a possible conversion of an embedded Pdu value into -- to the enclosing Pdu. -- -- This generates better compiler error messages, when an embedding of a -- Pdu into another. -- -- This is provided by HasPdu instances. The instances are -- required to provide a list of embedded Pdu values in -- EmbeddedPduList. -- -- Note that every type embeds itself, so Embeds x x always -- holds. type Embeds outer inner = (HasPduPrism outer inner, CheckEmbeds outer inner, HasPdu outer) -- | This type class and the associated data family defines the protocol -- data units (PDU) of a protocol. -- -- A Protocol in the sense of a communication interface description -- between processes. -- -- The first parameter is usually a user defined type that identifies the -- protocol that uses the Pdus are. It maybe a phantom -- type. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Pdu instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   instance Typeable r => HasPdu BookShop r where
--     data instance Pdu BookShop r where
--       RentBook  :: BookId   -> Pdu BookShop ('Synchronous (Either RentalError RentalId))
--       BringBack :: RentalId -> Pdu BookShop 'Asynchronous
--       deriving Typeable
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
class Typeable protocol => HasPdu (protocol :: Type) where { -- | A type level list Protocol phantom types included in the associated -- Pdu instance. -- -- This is just a helper for better compiler error messages. It relies on -- Embeds to add the constraint HasPduPrism. type family EmbeddedPduList protocol :: [Type]; -- | The protocol data unit type for the given protocol. data family Pdu protocol (reply :: Synchronicity); type EmbeddedPduList protocol = '[]; } -- | A server process for protocol. -- -- Protocols are represented by phantom types, which are used in -- different places to index type families and type class instances. -- -- A Process can send and receive any messages. An Endpoint -- wraps around a ProcessId and carries a phantom type to indicate -- the kinds of messages accepted by the process. -- -- As a metaphor, communication between processes can be thought of -- waiting for and sending protocol data units belonging to some -- protocol. newtype Endpoint protocol Endpoint :: ProcessId -> Endpoint protocol [_fromEndpoint] :: Endpoint protocol -> ProcessId -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API proxyAsEndpoint :: proxy protocol -> ProcessId -> Endpoint protocol -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API asEndpoint :: forall protocol. ProcessId -> Endpoint protocol -- | Convert an Endpoint to an endpoint for an embedded protocol. -- -- See Embeds, fromEmbeddedEndpoint. toEmbeddedEndpoint :: forall inner outer. Embeds outer inner => Endpoint outer -> Endpoint inner -- | Convert an Endpoint to an endpoint for a server, that embeds -- the protocol. -- -- See Embeds, toEmbeddedEndpoint. fromEmbeddedEndpoint :: forall outer inner. HasPduPrism outer inner => Endpoint inner -> Endpoint outer fromEndpoint :: forall protocol_aUDz protocol_aVLX. Iso (Endpoint protocol_aUDz) (Endpoint protocol_aVLX) ProcessId ProcessId -- | The reader effect for ProcessIds for Pdus, see -- runEndpointReader type EndpointReader o = Reader (Endpoint o) -- | Instead of passing around a Endpoint value and passing to -- functions like cast or call, a Endpoint can -- provided by a Reader effect, if there is only a single -- server for a given Pdu instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Endpoint. type HasEndpointReader o r = (Typeable o, Member (EndpointReader o) r) -- | Send a request Pdu that has no reply and return immediately. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasCallStack, HasProcesses r q, HasPdu destination, HasPdu protocol, Tangible (Pdu destination 'Asynchronous), Embeds destination protocol) => Endpoint destination -> Pdu protocol 'Asynchronous -> Eff r () -- | Send a request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu clause -- is Synchronous. -- -- Always prefer callWithTimeout over call call :: forall result destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Embeds destination protocol, HasCallStack) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Eff r result -- | Send an request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Member Logs r, HasCallStack, Embeds destination protocol) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Timeout -> Eff r result -- | Run a reader effect that contains the one server handling a -- specific Pdu instance. runEndpointReader :: HasCallStack => Endpoint o -> Eff (EndpointReader o : r) a -> Eff r a -- | Get the Endpoint registered with runEndpointReader. askEndpoint :: Member (EndpointReader o) e => Eff e (Endpoint o) -- | Like call but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use callSingleton. callEndpointReader :: forall reply o r q. (HasEndpointReader o r, HasCallStack, Tangible reply, TangiblePdu o ( 'Synchronous reply), HasProcesses r q, Embeds o o) => Pdu o ( 'Synchronous reply) -> Eff r reply -- | Like cast but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use castSingleton. castEndpointReader :: forall o r q. (HasEndpointReader o r, HasProcesses r q, Tangible (Pdu o 'Asynchronous), HasCallStack, HasPdu o, Embeds o o) => Pdu o 'Asynchronous -> Eff r () -- | Like callEndpointReader, uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- callEndpointReader. callSingleton :: forall outer inner reply q e. (HasCallStack, Member (EndpointReader outer) e, Embeds outer inner, Embeds outer outer, HasProcesses e q, TangiblePdu outer ( 'Synchronous reply), TangiblePdu inner ( 'Synchronous reply), Tangible reply) => Pdu inner ( 'Synchronous reply) -> Eff e reply -- | Like castEndpointReader, but uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- castEndpointReader. castSingleton :: forall outer inner q e. (HasCallStack, Member (EndpointReader outer) e, Tangible (Pdu outer 'Asynchronous), HasProcesses e q, HasPdu outer, HasPdu inner, Embeds outer inner, Embeds outer outer) => Pdu inner 'Asynchronous -> Eff e () -- | Target of a Call reply. -- -- This combines a RequestOrigin with a Serializer for a -- Reply using Arg. There are to smart constructors for -- this type: replyTarget and embeddedReplyTarget. -- -- Because of Arg the Eq and Ord instances are -- implemented via the RequestOrigin instances. newtype ReplyTarget p r MkReplyTarget :: Arg (RequestOrigin p r) (Serializer (Reply p r)) -> ReplyTarget p r -- | Wraps the source ProcessId and a unique identifier for a -- Call. data RequestOrigin (proto :: Type) reply RequestOrigin :: !ProcessId -> !Int -> RequestOrigin reply [_requestOriginPid] :: RequestOrigin reply -> !ProcessId [_requestOriginCallRef] :: RequestOrigin reply -> !Int -- | The wrapper around replies to Calls. data Reply protocol reply [Reply] :: Tangible reply => {_replyTo :: RequestOrigin protocol reply, _replyValue :: reply} -> Reply protocol reply -- | A wrapper sum type for calls and casts for the Pdus of a -- protocol data Request protocol [Call] :: forall protocol reply. (Tangible reply, TangiblePdu protocol ( 'Synchronous reply)) => RequestOrigin protocol reply -> Pdu protocol ( 'Synchronous reply) -> Request protocol [Cast] :: forall protocol. (TangiblePdu protocol 'Asynchronous, NFData (Pdu protocol 'Asynchronous)) => Pdu protocol 'Asynchronous -> Request protocol -- | Create a new, unique RequestOrigin value for the current -- process. makeRequestOrigin :: (Typeable r, NFData r, HasProcesses e q0) => Eff e (RequestOrigin p r) -- | Turn an RequestOrigin to an origin for an embedded request (See -- Embeds). -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also embedReplySerializer. toEmbeddedOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin outer reply -> RequestOrigin inner reply -- | Turn an embedded RequestOrigin to a RequestOrigin -- for the bigger request. -- -- This is the inverse of toEmbeddedOrigin. -- -- This function is strict in all parameters. embedRequestOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin inner reply -> RequestOrigin outer reply -- | Turn a Serializer for a Pdu instance that contains -- embedded Pdu values into a Reply Serializer for -- the embedded Pdu. -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also toEmbeddedOrigin. embedReplySerializer :: forall outer inner reply. Embeds outer inner => Serializer (Reply outer reply) -> Serializer (Reply inner reply) -- | Answer a Call by sending the reply value to the client process. -- -- The ProcessId, the RequestOrigin and the Reply -- Serializer are stored in the ReplyTarget. sendReply :: (HasProcesses eff q, Tangible reply, Typeable protocol) => ReplyTarget protocol reply -> reply -> Eff eff () -- | Smart constructor for a ReplyTarget. -- -- To build a ReplyTarget for an Embeds instance use -- embeddedReplyTarget. replyTarget :: Serializer (Reply p reply) -> RequestOrigin p reply -> ReplyTarget p reply -- | A simple Lens for the RequestOrigin of a -- ReplyTarget. replyTargetOrigin :: Lens' (ReplyTarget p reply) (RequestOrigin p reply) -- | A simple Lens for the Reply Serializer of a -- ReplyTarget. replyTargetSerializer :: Lens' (ReplyTarget p reply) (Serializer (Reply p reply)) -- | Smart constructor for an embedded ReplyTarget. -- -- This combines replyTarget and toEmbeddedReplyTarget. embeddedReplyTarget :: Embeds outer inner => Serializer (Reply outer reply) -> RequestOrigin outer reply -> ReplyTarget inner reply -- | Convert a ReplyTarget to be usable for embedded replies. -- -- This combines a toEmbeddedOrigin with -- embedReplySerializer to produce a ReplyTarget that can -- be passed to functions defined soley on an embedded protocol. toEmbeddedReplyTarget :: Embeds outer inner => ReplyTarget outer reply -> ReplyTarget inner reply -- | The protocol data unit type for the given protocol. data family Pdu protocol (reply :: Synchronicity) -- | Alias for the effect that contains the observers managed by -- evalObserverRegistryState type ObserverRegistryState event = State (ObserverRegistry event) -- | A protocol for managing Observers, encompassing registration -- and de-registration of Observers. data ObserverRegistry (event :: Type) MkObserverRegistry :: Map ProcessId (ObservationSink event) -> ObserverRegistry [_observerRegistry] :: ObserverRegistry -> Map ProcessId (ObservationSink event) -- | Convenience type alias. type CanObserve eventSink event = (Tangible event, Embeds eventSink (Observer event), HasPdu eventSink) -- | Convenience type alias. type IsObservable eventSource event = (Tangible event, Embeds eventSource (ObserverRegistry event), HasPdu eventSource) -- | The Information necessary to wrap an Observed event to a -- process specific message, e.g. the embedded Observer Pdu -- instance, and the MonitorReference of the destination process. data ObservationSink event -- | A protocol to communicate Observed events from a sources -- to many sinks. -- -- A sink is any process that serves a protocol with a Pdu -- instance that embeds the Observer Pdu via an HasPduPrism -- instance. -- -- This type has dual use, for one it serves as type-index for -- Pdu, i.e. HasPdu respectively, and secondly it contains -- an ObservationSink and a MonitorReference. -- -- The ObservationSink is used to serialize and send the -- Observed events, while the ProcessId serves as key for -- internal maps. newtype Observer event MkObserver :: Arg ProcessId (ObservationSink event) -> Observer event -- | And an Observer to the set of recipients for all observations -- reported by observerRegistryNotify. Note that the -- observerRegistry 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 :: forall event eventSink eventSource r q. (HasCallStack, HasProcesses r q, IsObservable eventSource event, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message forgetObserver :: forall event eventSink eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), IsObservable eventSource event, CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message, use a raw ProcessId as -- parameter. forgetObserverUnsafe :: forall event eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), IsObservable eventSource event) => Endpoint eventSource -> ProcessId -> Eff r () -- | Provide the implementation for the ObserverRegistry Protocol, -- this handled RegisterObserver and ForgetObserver -- messages. It also adds the ObserverRegistryState constraint to -- the effect list. observerRegistryHandlePdu :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => Pdu (ObserverRegistry event) 'Asynchronous -> Eff r () -- | Remove the entry in the ObserverRegistry for the -- ProcessId and return True if there was an entry, -- False otherwise. observerRegistryRemoveProcess :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => ProcessId -> Eff r Bool -- | Keep track of registered Observers. -- -- Handle the ObserverRegistryState effect, i.e. run -- evalState on an emptyObserverRegistry. evalObserverRegistryState :: HasCallStack => Eff (ObserverRegistryState event : r) a -> Eff r a -- | The empty ObserverRegistryState emptyObserverRegistry :: ObserverRegistry event -- | Report an observation to all observers. The process needs to -- evalObserverRegistryState and to -- observerRegistryHandlePdu. observerRegistryNotify :: forall event r q. (HasProcesses r q, Member (ObserverRegistryState event) r, Tangible event, HasCallStack) => event -> Eff r () -- | Concurrent, communicating processes, executed using a single-threaded -- scheduler, with support for IO and Logs. -- -- This module re-exports most of the library. -- -- There are several scheduler implementations to choose from. -- -- This module re-exports the impure parts of -- Control.Eff.Concurrent.Process.SingleThreadedScheduler. -- -- To use another scheduler implementation, don't import this module, but -- instead import one of: -- -- module Control.Eff.Concurrent.SingleThreaded -- | An rfc 5424 facility newtype Facility Facility :: Int -> Facility [fromFacility] :: Facility -> Int -- | An rfc 5424 severity data Severity -- | Component of an RFC-5424 StructuredDataElement data SdParameter MkSdParameter :: !Text -> !Text -> SdParameter -- | 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] -- | 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 -- | 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 -- | 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 -- | 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 -- | The filter predicate for message that shall be logged. -- -- See Control.Eff.Log#LogPredicate type LogPredicate = LogMessage -> Bool -- | Things that can become a LogMessage class ToLogMessage a -- | Convert the value to a LogMessage toLogMessage :: ToLogMessage a => a -> 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 the Facility of a LogMessage lmFacility :: Functor f => (Facility -> f Facility) -> 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 user defined textual message of a LogMessage lmMessage :: Functor f => (Text -> f 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 a user defined of process id of a LogMessage lmProcessId :: Functor f => (Maybe Text -> f (Maybe Text)) -> LogMessage -> f LogMessage -- | A lens for the Severity of a LogMessage lmSeverity :: Functor f => (Severity -> f Severity) -> 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 StructuredDataElement of a LogMessage lmStructuredData :: Functor f => ([StructuredDataElement] -> f [StructuredDataElement]) -> 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 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 -- | 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 -- | 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 -- | 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 -- | A rendering function for the lmTimestamp field. data LogMessageTimeRenderer -- | LogMessage to Text rendering function. type LogMessageTextRenderer = LogMessageRenderer Text -- | LogMessage rendering function type LogMessageRenderer a = LogMessage -> a -- | 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 -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBody :: LogMessageTextRenderer -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBodyNoLocation :: LogMessageTextRenderer -- | Print the body of a LogMessage with fix size fields (60) -- for the message itself and 30 characters for the location renderLogMessageBodyFixWidth :: LogMessageTextRenderer -- | Render a field of a LogMessage using the corresponsing lens. renderMaybeLogMessageLens :: Text -> Getter LogMessage (Maybe Text) -> LogMessageTextRenderer -- | Render the source location as: at filepath:linenumber. renderLogMessageSrcLoc :: LogMessageRenderer (Maybe 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 :: LogMessageTextRenderer -- | 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 :: LogMessageTextRenderer -- | Render a LogMessage human readable, for console logging renderLogMessageConsoleLog :: LogMessageTextRenderer -- | Render a LogMessage human readable, for console logging renderConsoleMinimalisticWide :: LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-3164. renderRFC3164 :: LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-3164 but -- use RFC5424 time stamps. renderRFC3164WithRFC5424Timestamps :: LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-3164 but -- use the custom LogMessageTimeRenderer. renderRFC3164WithTimestamp :: LogMessageTimeRenderer -> LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-5424. -- -- Equivalent to renderRFC5424Header <> const " " -- <> renderLogMessageBody. renderRFC5424 :: LogMessageTextRenderer -- | 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 :: LogMessageTextRenderer -- | Render the header and strucuted data of a LogMessage according -- to the rules in the RFC-5424, but do not render the lmMessage. renderRFC5424Header :: LogMessageTextRenderer -- | A Reader effect for LogWriters. type LogWriterReader = Reader LogWriter -- | A function that takes a log message and returns an effect that -- logs the message. newtype LogWriter MkLogWriter :: (LogMessage -> IO ()) -> LogWriter [runLogWriter] :: LogWriter -> LogMessage -> IO () -- | Provide the LogWriter -- -- Exposed for custom extensions, if in doubt use withLogging. runLogWriterReader :: LogWriter -> Eff (Reader LogWriter : e) a -> Eff e a -- | Get the current LogWriter. askLogWriter :: Member LogWriterReader e => Eff e LogWriter -- | Modify the current LogWriter. localLogWriterReader :: forall e a. Member LogWriterReader e => (LogWriter -> LogWriter) -> Eff e a -> Eff e a -- | Write a message using the LogWriter found in the environment. liftWriteLogMessage :: (Member LogWriterReader e, Lifted IO e) => LogMessage -> Eff e () -- | This LogWriter will discard all messages. -- -- NOTE: This is just an alias for mempty noOpLogWriter :: LogWriter -- | A LogWriter that applies a predicate to the LogMessage -- and delegates to to the given writer of the predicate is satisfied. filteringLogWriter :: LogPredicate -> LogWriter -> LogWriter -- | A LogWriter that applies a function to the LogMessage -- and delegates the result to to the given writer. mappingLogWriter :: (LogMessage -> LogMessage) -> LogWriter -> LogWriter -- | Like mappingLogWriter allow the function that changes the -- LogMessage to have effects. mappingLogWriterIO :: (LogMessage -> IO LogMessage) -> LogWriter -> LogWriter -- | Append the LogMessage to an Handle after rendering it. ioHandleLogWriter :: Handle -> LogMessageRenderer Text -> LogWriter -- | Render a LogMessage to stdout. -- -- This function will also set the BufferMode of stdout to -- LineBuffering. -- -- See ioHandleLogWriter. stdoutLogWriter :: LogMessageRenderer Text -> IO LogWriter -- | The concrete list of Effects for logging with a -- LogWriter, and a LogWriterReader. -- -- This also provides both IoLogging and FilteredLogging. type LoggingAndIo = '[Logs, LogWriterReader, Lift IO] -- | A constraint that requires Logs and LogWriterReader, and -- hence supports the functions to filter and modify logs: -- -- -- -- Provided by withLogging, runLogs, and also -- withoutLogging and runLogsWithoutLogging. type FilteredLogging e = (Member Logs e, Member LogWriterReader e) -- | A constraint that requires Logs e and -- Lifted IO e. -- -- Provided by withLogging and runLogs. -- -- It contains FilteredLogging and allows in addition: -- -- -- -- Don't infect everything with IO, if you can fall back to -- FilteredLogging. type IoLogging e = (FilteredLogging e, Lifted IO e) -- | 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 -- | 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"
--   
-- -- This provides the IoLogging and FilteredLogging effects. -- -- See also runLogs. withLogging :: Lifted IO e => LogWriter -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Handles the Logs and LogWriterReader effects, while not -- invoking the LogWriter at all. -- -- There is no way to get log output when this logger is used. -- -- Example: -- --
--   exampleWithSomeLogging :: ()
--   exampleWithSomeLogging =
--       run
--     $ withoutLogging
--     $ logDebug "Oh, hi there" -- Nothing written
--   
-- -- This provides the FilteredLogging effect. -- -- See also runLogsWithoutLogging. withoutLogging :: Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withLogging. runLogs :: forall e b. (Member LogWriterReader (Logs : e), Lifted IO e) => LogPredicate -> Eff (Logs : e) b -> Eff e b -- | 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 message with alertSeverity. logAlert :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a criticalSeverity message. logCritical :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a errorSeverity message. logError :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a warningSeverity message. logWarning :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a noticeSeverity message. logNotice :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a informationalSeverity message. logInfo :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a debugSeverity message. logDebug :: 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) => String -> 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) => String -> 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) => String -> 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) => String -> Eff e () -- | Log the current callStack using the given Severity. logCallStack :: forall e. (HasCallStack, Member Logs e) => Severity -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multi line log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. logMultiLine :: forall e. (HasCallStack, Member Logs e) => Severity -> [Text] -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multiline log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. -- -- This function takes a list of Strings as opposed to -- logMultiLine. logMultiLine' :: forall e. (HasCallStack, Member Logs e) => Severity -> [String] -> Eff e () -- | Get the current Logs filter/transformer function. -- -- See Control.Eff.Log#LogPredicate askLogPredicate :: forall e. Member Logs e => Eff e LogPredicate -- | Keep only those messages, for which a predicate holds. -- -- E.g. to keep only messages which begin with OMG: -- --
--   exampleSetLogWriter :: IO Int
--   exampleSetLogWriter =
--       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 -- | Include LogMessages that match a LogPredicate. -- -- includeLogMessages 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 -- | 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 -- | Change the current LogWriter. modifyLogWriter :: IoLogging e => (LogWriter -> LogWriter) -> Eff e a -> Eff e a -- | Replace the current LogWriter. To add an additional log message -- consumer use addLogWriter setLogWriter :: IoLogging e => LogWriter -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriter censorLogs :: IoLogging 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 . -- mappingLogWriterIO censorLogsIo :: IoLogging e => (LogMessage -> IO LogMessage) -> 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 :: IoLogging e => LogWriter -> Eff e a -> Eff e a -- | 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 -> !Interrupt 'NoRecovery -> !ProcessId -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !Interrupt 'NoRecovery [downProcess] :: ProcessDown -> !ProcessId -- | 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 -- | Exceptions containing Interrupts. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc (Interrupt 'Recoverable) -- | A constraint for an effect set that requires the presence of -- SafeProcesses. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Process [e(N+1) .. e(N+k)], e(N+1) .. e(N+k)] -- -- It constrains e to support the (only) Process effect. -- -- This is more relaxed that HasProcesses since it does not -- require Interrupts. type HasSafeProcesses e inner = (SetMember Process (Process inner) e) -- | Cons Process onto a list of effects. This is called -- SafeProcesses because the the actions cannot be interrupted -- in. type SafeProcesses r = Process r : r -- | A constraint for an effect set that requires the presence of -- Processes. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Interrupts, Process [e(N+1) .. e(N+k)], e(N+1) .. -- e(N+k)] -- -- It constrains e beyond HasSafeProcesses to encompass -- Interrupts. type HasProcesses e inner = (HasSafeProcesses e inner, Member Interrupts e) -- | This adds a layer of the Interrupts effect on top of -- Processes type Processes e = Interrupts : SafeProcesses e -- | 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 -- | Extension of ExitNormally with a custom reason [NormalExitRequestedWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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, see Link for a discussion on linking. [LinkedProcessCrashed] :: ProcessId -> Interrupt 'Recoverable -- | An exit reason that has an error message and is Recoverable. [ErrorInterrupt] :: String -> Interrupt 'Recoverable -- | An interrupt with a custom message. [InterruptedBy] :: forall a. (Typeable a, Show a, NFData a) => a -> Interrupt 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: Interrupt 'NoRecovery -- | A process function returned or exited without any error, and with a -- custom message [ExitNormallyWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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 -- recovered. [ExitUnhandledError] :: Text -> Interrupt 'NoRecovery -- | A process shall exit immediately, without any cleanup was cancelled -- (e.g. killed, in cancel) [ExitProcessCancelled] :: Maybe ProcessId -> Interrupt 'NoRecovery -- | A process that is vital to the crashed process was not running [ExitOtherProcessNotRunning] :: ProcessId -> 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 -- | 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 -- | Serialize a message into a StrictDynamic value to be -- sent via sendAnyMessage. -- -- This indirection allows, among other things, the composition of -- Servers. newtype Serializer message MkSerializer :: (message -> StrictDynamic) -> Serializer message [runSerializer] :: Serializer message -> message -> StrictDynamic -- | 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 -- | A number of micro seconds. newtype Timeout TimeoutMicros :: Int -> Timeout [fromTimeoutMicros] :: Timeout -> Int -- | A multi-line text describing the current state of a process for -- debugging purposes. newtype ProcessDetails MkProcessDetails :: Text -> ProcessDetails [_fromProcessDetails] :: ProcessDetails -> Text -- | A short title for a Process for logging purposes. newtype ProcessTitle MkProcessTitle :: Text -> ProcessTitle [_fromProcessTitle] :: ProcessTitle -> Text -- | 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 ()) -- | Simply wait until the time in the given Timeout has elapsed and -- return. [Delay] :: Timeout -> 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] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | 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. -- -- You might wonder: Why not tearing down the linked process when exiting -- normally? I thought about this. If a process exits normally, it should -- have the opportunity to shutdown stuff explicitly. And if you want to -- make sure that there are no dangling child processes after e.g. a -- broker crash, you can always use monitor. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Update the ProcessDetails of a process [UpdateProcessDetails] :: ProcessDetails -> Process r (ResumeProcess ()) -- | Get the ProcessState (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe (ProcessTitle, ProcessDetails, ProcessState))) -- | An isomorphism lens for the ProcessTitle fromProcessTitle :: Lens' ProcessTitle Text -- | An isomorphism lens for the ProcessDetails fromProcessDetails :: Lens' ProcessDetails Text -- | 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 Processes by wrapping them -- up in interruptToExit and then do a process Shutdown. provideInterruptsShutdown :: forall e a. Eff (Processes e) a -> Eff (SafeProcesses 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, HasProcesses r q) => 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasProcesses r q, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Use executeAndResumeOrExit to execute YieldProcess. -- Refer to YieldProcess for more information. yieldProcess :: forall r q. (HasProcesses r q, HasCallStack) => Eff r () -- | Simply block until the time in the Timeout has passed. delay :: forall r q. (HasProcesses r q, HasCallStack) => Timeout -> 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 o r q. (HasProcesses r q, HasCallStack, 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. (HasCallStack, HasProcesses r q) => 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. (HasCallStack, HasProcesses r q) => 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. (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r () -- | Start a new process, and immediately link to it. -- -- See Link for a discussion on linking. spawnLink :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes 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 SafeProcesses effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff r () -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r Bool -- | Return the ProcessTitle, ProcessDetails and -- ProcessState, for the given process, if the process is alive. getProcessState :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r (Maybe (ProcessTitle, ProcessDetails, ProcessState)) -- | Replace the ProcessDetails of the process. updateProcessDetails :: forall r q. (HasCallStack, HasProcesses r q) => ProcessDetails -> Eff r () -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => 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, HasProcesses r q) => Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, HasProcesses r q) => 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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, HasSafeProcesses r q) => Eff r ProcessId -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, HasProcesses r q) => MonitorReference -> Eff r () -- | monitor another process before while performing an action and -- demonitor afterwards. withMonitor :: (HasCallStack, HasProcesses r q) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, HasProcesses r q, 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. -- -- The parameter is the value obtained by monitor. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | A MessageSelector for the ProcessDown message. of a -- specific process. -- -- In contrast to selectProcessDown this function matches the -- ProcessId. selectProcessDownByProcessId :: ProcessId -> 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. -- -- See Link for a discussion on linking. linkProcess :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. unlinkProcess :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r () -- | Exit the process with a Interrupt. exitBecause :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Interrupt 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, HasSafeProcesses r q) => String -> Eff r a -- | A ProcessId and a Serializer. EXPERIMENTAL -- -- See sendToReceiver. data Receiver a Receiver :: ProcessId -> (a -> out) -> Receiver a [_receiverPid] :: Receiver a -> ProcessId [_receiverSerializer] :: Receiver a -> a -> out fromProcessId :: Iso' ProcessId Int -- | Serialize and send a message to the process in a Receiver. -- -- EXPERIMENTAL sendToReceiver :: (NFData o, HasProcesses r q) => Receiver o -> o -> Eff r () receiverPid :: forall a_aHaF. Lens' (Receiver a_aHaF) ProcessId -- | A class for Pdu instances that embed other Pdu. -- -- This is a part of Embeds provide instances for your Pdus -- but in client code use the Embeds constraint. -- -- Instances of this class serve as proof to Embeds that a -- conversion into another Pdu actually exists. -- -- A Prism for the embedded Pdu is the center of this class -- -- Laws: embeddedPdu = prism' embedPdu fromPdu class (Typeable protocol, Typeable embeddedProtocol) => HasPduPrism protocol embeddedProtocol -- | A Prism for the embedded Pdus. embeddedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Prism' (Pdu protocol result) (Pdu embeddedProtocol result) -- | Embed the Pdu value of an embedded protocol into the -- corresponding Pdu value. embedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu embeddedProtocol result -> Pdu protocol result -- | Examine a Pdu value from the outer protocol, and return it, if -- it embeds a Pdu of embedded protocol, otherwise return -- Nothing/ fromPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu protocol result -> Maybe (Pdu embeddedProtocol result) -- | This type function takes an Pdu and analysis the reply type, -- i.e. the Synchronicity and evaluates to either t for -- an Pdu x (Synchronous t) or to '()' for an Pdu x -- Asynchronous. type family ProtocolReply (s :: Synchronicity) -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Pdu -- 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 Constraint that bundles the requirements for the Pdu -- values of a protocol. -- -- This ensures that Pdus can be strictly and deeply evaluated and -- shown such that for example logging is possible. type TangiblePdu p r = (Typeable p, Typeable r, Tangible (Pdu p r), HasPdu p) -- | 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. type Tangible i = (NFData i, Typeable i, Show i) -- | A constraint that requires that the outer Pdu has a -- clause to embed values from the inner Pdu. -- -- Also, this constraint requires a HasPduPrism instance, as a -- proof for a possible conversion of an embedded Pdu value into -- to the enclosing Pdu. -- -- This generates better compiler error messages, when an embedding of a -- Pdu into another. -- -- This is provided by HasPdu instances. The instances are -- required to provide a list of embedded Pdu values in -- EmbeddedPduList. -- -- Note that every type embeds itself, so Embeds x x always -- holds. type Embeds outer inner = (HasPduPrism outer inner, CheckEmbeds outer inner, HasPdu outer) -- | This type class and the associated data family defines the protocol -- data units (PDU) of a protocol. -- -- A Protocol in the sense of a communication interface description -- between processes. -- -- The first parameter is usually a user defined type that identifies the -- protocol that uses the Pdus are. It maybe a phantom -- type. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Pdu instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   instance Typeable r => HasPdu BookShop r where
--     data instance Pdu BookShop r where
--       RentBook  :: BookId   -> Pdu BookShop ('Synchronous (Either RentalError RentalId))
--       BringBack :: RentalId -> Pdu BookShop 'Asynchronous
--       deriving Typeable
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
class Typeable protocol => HasPdu (protocol :: Type) where { -- | A type level list Protocol phantom types included in the associated -- Pdu instance. -- -- This is just a helper for better compiler error messages. It relies on -- Embeds to add the constraint HasPduPrism. type family EmbeddedPduList protocol :: [Type]; -- | The protocol data unit type for the given protocol. data family Pdu protocol (reply :: Synchronicity); type EmbeddedPduList protocol = '[]; } -- | A server process for protocol. -- -- Protocols are represented by phantom types, which are used in -- different places to index type families and type class instances. -- -- A Process can send and receive any messages. An Endpoint -- wraps around a ProcessId and carries a phantom type to indicate -- the kinds of messages accepted by the process. -- -- As a metaphor, communication between processes can be thought of -- waiting for and sending protocol data units belonging to some -- protocol. newtype Endpoint protocol Endpoint :: ProcessId -> Endpoint protocol [_fromEndpoint] :: Endpoint protocol -> ProcessId -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API proxyAsEndpoint :: proxy protocol -> ProcessId -> Endpoint protocol -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API asEndpoint :: forall protocol. ProcessId -> Endpoint protocol -- | Convert an Endpoint to an endpoint for an embedded protocol. -- -- See Embeds, fromEmbeddedEndpoint. toEmbeddedEndpoint :: forall inner outer. Embeds outer inner => Endpoint outer -> Endpoint inner -- | Convert an Endpoint to an endpoint for a server, that embeds -- the protocol. -- -- See Embeds, toEmbeddedEndpoint. fromEmbeddedEndpoint :: forall outer inner. HasPduPrism outer inner => Endpoint inner -> Endpoint outer fromEndpoint :: forall protocol_aUDz protocol_aVLX. Iso (Endpoint protocol_aUDz) (Endpoint protocol_aVLX) ProcessId ProcessId -- | Target of a Call reply. -- -- This combines a RequestOrigin with a Serializer for a -- Reply using Arg. There are to smart constructors for -- this type: replyTarget and embeddedReplyTarget. -- -- Because of Arg the Eq and Ord instances are -- implemented via the RequestOrigin instances. newtype ReplyTarget p r MkReplyTarget :: Arg (RequestOrigin p r) (Serializer (Reply p r)) -> ReplyTarget p r -- | Wraps the source ProcessId and a unique identifier for a -- Call. data RequestOrigin (proto :: Type) reply RequestOrigin :: !ProcessId -> !Int -> RequestOrigin reply [_requestOriginPid] :: RequestOrigin reply -> !ProcessId [_requestOriginCallRef] :: RequestOrigin reply -> !Int -- | The wrapper around replies to Calls. data Reply protocol reply [Reply] :: Tangible reply => {_replyTo :: RequestOrigin protocol reply, _replyValue :: reply} -> Reply protocol reply -- | A wrapper sum type for calls and casts for the Pdus of a -- protocol data Request protocol [Call] :: forall protocol reply. (Tangible reply, TangiblePdu protocol ( 'Synchronous reply)) => RequestOrigin protocol reply -> Pdu protocol ( 'Synchronous reply) -> Request protocol [Cast] :: forall protocol. (TangiblePdu protocol 'Asynchronous, NFData (Pdu protocol 'Asynchronous)) => Pdu protocol 'Asynchronous -> Request protocol -- | Create a new, unique RequestOrigin value for the current -- process. makeRequestOrigin :: (Typeable r, NFData r, HasProcesses e q0) => Eff e (RequestOrigin p r) -- | Turn an RequestOrigin to an origin for an embedded request (See -- Embeds). -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also embedReplySerializer. toEmbeddedOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin outer reply -> RequestOrigin inner reply -- | Turn an embedded RequestOrigin to a RequestOrigin -- for the bigger request. -- -- This is the inverse of toEmbeddedOrigin. -- -- This function is strict in all parameters. embedRequestOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin inner reply -> RequestOrigin outer reply -- | Turn a Serializer for a Pdu instance that contains -- embedded Pdu values into a Reply Serializer for -- the embedded Pdu. -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also toEmbeddedOrigin. embedReplySerializer :: forall outer inner reply. Embeds outer inner => Serializer (Reply outer reply) -> Serializer (Reply inner reply) -- | Answer a Call by sending the reply value to the client process. -- -- The ProcessId, the RequestOrigin and the Reply -- Serializer are stored in the ReplyTarget. sendReply :: (HasProcesses eff q, Tangible reply, Typeable protocol) => ReplyTarget protocol reply -> reply -> Eff eff () -- | Smart constructor for a ReplyTarget. -- -- To build a ReplyTarget for an Embeds instance use -- embeddedReplyTarget. replyTarget :: Serializer (Reply p reply) -> RequestOrigin p reply -> ReplyTarget p reply -- | A simple Lens for the RequestOrigin of a -- ReplyTarget. replyTargetOrigin :: Lens' (ReplyTarget p reply) (RequestOrigin p reply) -- | A simple Lens for the Reply Serializer of a -- ReplyTarget. replyTargetSerializer :: Lens' (ReplyTarget p reply) (Serializer (Reply p reply)) -- | Smart constructor for an embedded ReplyTarget. -- -- This combines replyTarget and toEmbeddedReplyTarget. embeddedReplyTarget :: Embeds outer inner => Serializer (Reply outer reply) -> RequestOrigin outer reply -> ReplyTarget inner reply -- | Convert a ReplyTarget to be usable for embedded replies. -- -- This combines a toEmbeddedOrigin with -- embedReplySerializer to produce a ReplyTarget that can -- be passed to functions defined soley on an embedded protocol. toEmbeddedReplyTarget :: Embeds outer inner => ReplyTarget outer reply -> ReplyTarget inner reply -- | 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 -- | 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. (HasCallStack, HasProcesses r q, 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. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitor combined with -- receiveSelectedAfter. receiveSelectedWithMonitorAfter :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> Eff r (Either (Either ProcessDown TimerElapsed) a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfterWithTitle. receiveAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Typeable a, NFData a, Show a) => Timeout -> ProcessTitle -> 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 startTimerWithTitle. receiveSelectedAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> ProcessTitle -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitorWithTitle combined with -- receiveSelectedAfterWithTitle. receiveSelectedWithMonitorAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> ProcessTitle -> 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. (HasCallStack, HasProcesses r q, Typeable message, NFData message) => ProcessId -> Timeout -> (TimerReference -> message) -> Eff r TimerReference -- | Like sendAfter but with a user provided name for the timer -- process. sendAfterWithTitle :: forall r q message. (HasCallStack, HasProcesses r q, Typeable message, NFData message) => ProcessTitle -> 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. -- -- This calls sendAfterWithTitle under the hood with -- TimerElapsed as message. startTimerWithTitle :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Timeout -> 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. -- -- Calls sendAfter under the hood. startTimer :: forall r q. (HasCallStack, HasProcesses r q) => Timeout -> Eff r TimerReference -- | Cancel a timer started with startTimer. cancelTimer :: forall r q. (HasCallStack, HasProcesses r q) => TimerReference -> Eff r () -- | The reader effect for ProcessIds for Pdus, see -- runEndpointReader type EndpointReader o = Reader (Endpoint o) -- | Instead of passing around a Endpoint value and passing to -- functions like cast or call, a Endpoint can -- provided by a Reader effect, if there is only a single -- server for a given Pdu instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Endpoint. type HasEndpointReader o r = (Typeable o, Member (EndpointReader o) r) -- | Send a request Pdu that has no reply and return immediately. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasCallStack, HasProcesses r q, HasPdu destination, HasPdu protocol, Tangible (Pdu destination 'Asynchronous), Embeds destination protocol) => Endpoint destination -> Pdu protocol 'Asynchronous -> Eff r () -- | Send a request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu clause -- is Synchronous. -- -- Always prefer callWithTimeout over call call :: forall result destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Embeds destination protocol, HasCallStack) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Eff r result -- | Send an request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Member Logs r, HasCallStack, Embeds destination protocol) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Timeout -> Eff r result -- | Run a reader effect that contains the one server handling a -- specific Pdu instance. runEndpointReader :: HasCallStack => Endpoint o -> Eff (EndpointReader o : r) a -> Eff r a -- | Get the Endpoint registered with runEndpointReader. askEndpoint :: Member (EndpointReader o) e => Eff e (Endpoint o) -- | Like call but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use callSingleton. callEndpointReader :: forall reply o r q. (HasEndpointReader o r, HasCallStack, Tangible reply, TangiblePdu o ( 'Synchronous reply), HasProcesses r q, Embeds o o) => Pdu o ( 'Synchronous reply) -> Eff r reply -- | Like cast but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use castSingleton. castEndpointReader :: forall o r q. (HasEndpointReader o r, HasProcesses r q, Tangible (Pdu o 'Asynchronous), HasCallStack, HasPdu o, Embeds o o) => Pdu o 'Asynchronous -> Eff r () -- | Like callEndpointReader, uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- callEndpointReader. callSingleton :: forall outer inner reply q e. (HasCallStack, Member (EndpointReader outer) e, Embeds outer inner, Embeds outer outer, HasProcesses e q, TangiblePdu outer ( 'Synchronous reply), TangiblePdu inner ( 'Synchronous reply), Tangible reply) => Pdu inner ( 'Synchronous reply) -> Eff e reply -- | Like castEndpointReader, but uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- castEndpointReader. castSingleton :: forall outer inner q e. (HasCallStack, Member (EndpointReader outer) e, Tangible (Pdu outer 'Asynchronous), HasProcesses e q, HasPdu outer, HasPdu inner, Embeds outer inner, Embeds outer outer) => Pdu inner 'Asynchronous -> Eff e () -- | Alias for the effect that contains the observers managed by -- evalObserverRegistryState type ObserverRegistryState event = State (ObserverRegistry event) -- | A protocol for managing Observers, encompassing registration -- and de-registration of Observers. data ObserverRegistry (event :: Type) MkObserverRegistry :: Map ProcessId (ObservationSink event) -> ObserverRegistry [_observerRegistry] :: ObserverRegistry -> Map ProcessId (ObservationSink event) -- | Convenience type alias. type CanObserve eventSink event = (Tangible event, Embeds eventSink (Observer event), HasPdu eventSink) -- | Convenience type alias. type IsObservable eventSource event = (Tangible event, Embeds eventSource (ObserverRegistry event), HasPdu eventSource) -- | The Information necessary to wrap an Observed event to a -- process specific message, e.g. the embedded Observer Pdu -- instance, and the MonitorReference of the destination process. data ObservationSink event -- | A protocol to communicate Observed events from a sources -- to many sinks. -- -- A sink is any process that serves a protocol with a Pdu -- instance that embeds the Observer Pdu via an HasPduPrism -- instance. -- -- This type has dual use, for one it serves as type-index for -- Pdu, i.e. HasPdu respectively, and secondly it contains -- an ObservationSink and a MonitorReference. -- -- The ObservationSink is used to serialize and send the -- Observed events, while the ProcessId serves as key for -- internal maps. newtype Observer event MkObserver :: Arg ProcessId (ObservationSink event) -> Observer event -- | And an Observer to the set of recipients for all observations -- reported by observerRegistryNotify. Note that the -- observerRegistry 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 :: forall event eventSink eventSource r q. (HasCallStack, HasProcesses r q, IsObservable eventSource event, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message forgetObserver :: forall event eventSink eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), IsObservable eventSource event, CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message, use a raw ProcessId as -- parameter. forgetObserverUnsafe :: forall event eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), IsObservable eventSource event) => Endpoint eventSource -> ProcessId -> Eff r () -- | Provide the implementation for the ObserverRegistry Protocol, -- this handled RegisterObserver and ForgetObserver -- messages. It also adds the ObserverRegistryState constraint to -- the effect list. observerRegistryHandlePdu :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => Pdu (ObserverRegistry event) 'Asynchronous -> Eff r () -- | Remove the entry in the ObserverRegistry for the -- ProcessId and return True if there was an entry, -- False otherwise. observerRegistryRemoveProcess :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => ProcessId -> Eff r Bool -- | Keep track of registered Observers. -- -- Handle the ObserverRegistryState effect, i.e. run -- evalState on an emptyObserverRegistry. evalObserverRegistryState :: HasCallStack => Eff (ObserverRegistryState event : r) a -> Eff r a -- | The empty ObserverRegistryState emptyObserverRegistry :: ObserverRegistry event -- | Report an observation to all observers. The process needs to -- evalObserverRegistryState and to -- observerRegistryHandlePdu. observerRegistryNotify :: forall event r q. (HasProcesses r q, Member (ObserverRegistryState event) r, Tangible event, HasCallStack) => event -> Eff r () -- | Enable logging to IO using the richLogWriter. -- -- Example: -- --
--   exampleWithIoLogging :: IO ()
--   exampleWithIoLogging =
--       runLift
--     $ withRichLogging debugTraceLogWriter
--                     "my-app"
--                     local7
--                     (lmSeverityIsAtLeast informationalSeverity)
--     $ logInfo "Oh, hi there"
--   
withRichLogging :: Lifted IO e => LogWriter -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Decorate an IO based LogWriter to fill out these fields in -- LogMessages: -- -- -- -- It works by using mappingLogWriterIO. richLogWriter :: Text -> Facility -> LogWriter -> LogWriter -- | Enable logging to a file, with some LogMessage fields preset as -- described in withRichLogging. -- -- 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 renderLogMessageConsoleLog
--     $ logInfo "Oh, hi there"
--   
-- -- To vary the LogWriter use withRichLogging. withFileLogging :: (Lifted IO e, MonadBaseControl IO (Eff e), HasCallStack) => FilePath -> Text -> Facility -> LogPredicate -> LogMessageTextRenderer -> Eff (Logs : (LogWriterReader : 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
--     $ withoutLogging
--     $ withFileLogWriter "test.log" renderLogMessageConsoleLog
--     $ logInfo "Oh, hi there"
--   
withFileLogWriter :: (IoLogging e, MonadBaseControl IO (Eff e), HasCallStack) => FilePath -> LogMessageTextRenderer -> Eff e b -> Eff e b -- | Enable logging via traceM using the debugTraceLogWriter, -- with some LogMessage fields preset as in -- withRichLogging. -- -- 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 : e)) a -> Eff e a -- | 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
--     $ withoutLogging @IO
--     $ withTraceLogWriter
--     $ logInfo "Oh, hi there"
--   
withTraceLogWriter :: IoLogging e => Eff e a -> Eff e a -- | Write LogMessages via traceM. debugTraceLogWriter :: LogMessageTextRenderer -> LogWriter -- | Enable logging to standard output using the -- consoleLogWriter, with some LogMessage fields preset as -- in withRichLogging. -- -- 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 withRichLogging. withConsoleLogging :: Lifted IO e => Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Enable logging to standard output using the -- consoleLogWriter. -- -- Log messages are rendered using renderLogMessageConsoleLog. -- -- Example: -- --
--   exampleWithConsoleLogWriter :: IO ()
--   exampleWithConsoleLogWriter =
--       runLift
--     $ withoutLogging @IO
--     $ withConsoleLogWriter
--     $ logInfo "Oh, hi there"
--   
withConsoleLogWriter :: IoLogging e => Eff e a -> Eff e a -- | Render a LogMessage to stdout using -- renderLogMessageConsoleLog. -- -- See stdoutLogWriter. consoleLogWriter :: IO LogWriter -- | This is a wrapper around withAsyncLogWriter and -- withRichLogging. -- -- 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 -> len -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | 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 :: (IoLogging e, MonadBaseControl IO (Eff e), Integral len) => len -> Eff e a -> Eff e a -- | Enable logging to a remote host via UDP, with some -- LogMessage fields preset as in withRichLogging. -- -- See exampleUdpRFC3164Logging withUDPLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => (LogMessage -> Text) -> String -> String -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Enable logging to a (remote-) host via UDP. -- -- See exampleUdpRFC3164Logging withUDPLogWriter :: (IoLogging e, MonadBaseControl IO (Eff e), HasCallStack) => (LogMessage -> Text) -> String -> String -> Eff e b -> Eff e b -- | Enable logging to a unix domain socket, with some -- LogMessage fields preset as in withRichLogging. -- -- See exampleDevLogSyslogLogging withUnixSocketLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => LogMessageRenderer Text -> FilePath -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Enable logging to a (remote-) host via UnixSocket. -- -- See exampleDevLogSyslogLogging withUnixSocketLogWriter :: (IoLogging e, MonadBaseControl IO (Eff e), HasCallStack) => LogMessageRenderer Text -> FilePath -> Eff e b -> Eff e b -- | 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 () -- | Run the Effects using a single threaded, coroutine based, -- scheduler from -- Control.Eff.Concurrent.Process.SingleThreadedScheduler. schedule :: HasCallStack => LogWriter -> Eff Effects a -> IO (Either (Interrupt 'NoRecovery) a) -- | Execute a Process using scheduleM on top of Lift -- IO. All logging is written to the console using -- consoleLogWriter. -- -- To use another LogWriter use defaultMainWithLogWriter -- instead. defaultMain :: HasCallStack => Eff EffectsIo () -> IO () -- | Execute a Process using scheduleM on top of Lift -- IO. All logging is written using the given LogWriter. defaultMainWithLogWriter :: HasCallStack => LogWriter -> Eff EffectsIo () -> IO () -- | The effect list for Process effects in the single threaded -- scheduler. -- -- See EffectsIo type Effects = EffectsIo -- | The effect list for Process effects in the single threaded -- scheduler. This is like SafeProcesses, no Interrupts are -- present. -- -- See SafeEffectsIo type SafeEffects = SafeEffectsIo -- | The effect list for the underlying scheduler. -- -- See BaseEffectsIo type BaseEffects = BaseEffectsIo -- | Constraint for the existence of the underlying scheduler effects. -- -- See HasBaseEffectsIo type HasBaseEffects e = HasBaseEffectsIo e -- | Concurrent, communicating processes, executed using a pure, -- single-threaded scheduler. -- -- This module re-exports most of the library. -- -- There are several scheduler implementations to choose from. -- -- This module re-exports the pure parts of -- Control.Eff.Concurrent.Process.SingleThreadedScheduler. -- -- To use another scheduler implementation, don't import this module, but -- instead import one of: -- -- module Control.Eff.Concurrent.Pure -- | An rfc 5424 facility newtype Facility Facility :: Int -> Facility [fromFacility] :: Facility -> Int -- | An rfc 5424 severity data Severity -- | Component of an RFC-5424 StructuredDataElement data SdParameter MkSdParameter :: !Text -> !Text -> SdParameter -- | 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] -- | 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 -- | 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 -- | 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 -- | 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 -- | The filter predicate for message that shall be logged. -- -- See Control.Eff.Log#LogPredicate type LogPredicate = LogMessage -> Bool -- | Things that can become a LogMessage class ToLogMessage a -- | Convert the value to a LogMessage toLogMessage :: ToLogMessage a => a -> 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 the Facility of a LogMessage lmFacility :: Functor f => (Facility -> f Facility) -> 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 user defined textual message of a LogMessage lmMessage :: Functor f => (Text -> f 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 a user defined of process id of a LogMessage lmProcessId :: Functor f => (Maybe Text -> f (Maybe Text)) -> LogMessage -> f LogMessage -- | A lens for the Severity of a LogMessage lmSeverity :: Functor f => (Severity -> f Severity) -> 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 StructuredDataElement of a LogMessage lmStructuredData :: Functor f => ([StructuredDataElement] -> f [StructuredDataElement]) -> 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 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 -- | 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 -- | 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 -- | 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 -- | A rendering function for the lmTimestamp field. data LogMessageTimeRenderer -- | LogMessage to Text rendering function. type LogMessageTextRenderer = LogMessageRenderer Text -- | LogMessage rendering function type LogMessageRenderer a = LogMessage -> a -- | 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 -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBody :: LogMessageTextRenderer -- | Print the thread id, the message and the source file location, -- seperated by simple white space. renderLogMessageBodyNoLocation :: LogMessageTextRenderer -- | Print the body of a LogMessage with fix size fields (60) -- for the message itself and 30 characters for the location renderLogMessageBodyFixWidth :: LogMessageTextRenderer -- | Render a field of a LogMessage using the corresponsing lens. renderMaybeLogMessageLens :: Text -> Getter LogMessage (Maybe Text) -> LogMessageTextRenderer -- | Render the source location as: at filepath:linenumber. renderLogMessageSrcLoc :: LogMessageRenderer (Maybe 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 :: LogMessageTextRenderer -- | 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 :: LogMessageTextRenderer -- | Render a LogMessage human readable, for console logging renderLogMessageConsoleLog :: LogMessageTextRenderer -- | Render a LogMessage human readable, for console logging renderConsoleMinimalisticWide :: LogMessageRenderer Text -- | Render a LogMessage according to the rules in the RFC-3164. renderRFC3164 :: LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-3164 but -- use RFC5424 time stamps. renderRFC3164WithRFC5424Timestamps :: LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-3164 but -- use the custom LogMessageTimeRenderer. renderRFC3164WithTimestamp :: LogMessageTimeRenderer -> LogMessageTextRenderer -- | Render a LogMessage according to the rules in the RFC-5424. -- -- Equivalent to renderRFC5424Header <> const " " -- <> renderLogMessageBody. renderRFC5424 :: LogMessageTextRenderer -- | 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 :: LogMessageTextRenderer -- | Render the header and strucuted data of a LogMessage according -- to the rules in the RFC-5424, but do not render the lmMessage. renderRFC5424Header :: LogMessageTextRenderer -- | A Reader effect for LogWriters. type LogWriterReader = Reader LogWriter -- | A function that takes a log message and returns an effect that -- logs the message. newtype LogWriter MkLogWriter :: (LogMessage -> IO ()) -> LogWriter [runLogWriter] :: LogWriter -> LogMessage -> IO () -- | Provide the LogWriter -- -- Exposed for custom extensions, if in doubt use withLogging. runLogWriterReader :: LogWriter -> Eff (Reader LogWriter : e) a -> Eff e a -- | Get the current LogWriter. askLogWriter :: Member LogWriterReader e => Eff e LogWriter -- | Modify the current LogWriter. localLogWriterReader :: forall e a. Member LogWriterReader e => (LogWriter -> LogWriter) -> Eff e a -> Eff e a -- | Write a message using the LogWriter found in the environment. liftWriteLogMessage :: (Member LogWriterReader e, Lifted IO e) => LogMessage -> Eff e () -- | This LogWriter will discard all messages. -- -- NOTE: This is just an alias for mempty noOpLogWriter :: LogWriter -- | A LogWriter that applies a predicate to the LogMessage -- and delegates to to the given writer of the predicate is satisfied. filteringLogWriter :: LogPredicate -> LogWriter -> LogWriter -- | A LogWriter that applies a function to the LogMessage -- and delegates the result to to the given writer. mappingLogWriter :: (LogMessage -> LogMessage) -> LogWriter -> LogWriter -- | Like mappingLogWriter allow the function that changes the -- LogMessage to have effects. mappingLogWriterIO :: (LogMessage -> IO LogMessage) -> LogWriter -> LogWriter -- | Append the LogMessage to an Handle after rendering it. ioHandleLogWriter :: Handle -> LogMessageRenderer Text -> LogWriter -- | Render a LogMessage to stdout. -- -- This function will also set the BufferMode of stdout to -- LineBuffering. -- -- See ioHandleLogWriter. stdoutLogWriter :: LogMessageRenderer Text -> IO LogWriter -- | The concrete list of Effects for logging with a -- LogWriter, and a LogWriterReader. -- -- This also provides both IoLogging and FilteredLogging. type LoggingAndIo = '[Logs, LogWriterReader, Lift IO] -- | A constraint that requires Logs and LogWriterReader, and -- hence supports the functions to filter and modify logs: -- -- -- -- Provided by withLogging, runLogs, and also -- withoutLogging and runLogsWithoutLogging. type FilteredLogging e = (Member Logs e, Member LogWriterReader e) -- | A constraint that requires Logs e and -- Lifted IO e. -- -- Provided by withLogging and runLogs. -- -- It contains FilteredLogging and allows in addition: -- -- -- -- Don't infect everything with IO, if you can fall back to -- FilteredLogging. type IoLogging e = (FilteredLogging e, Lifted IO e) -- | 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 -- | 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"
--   
-- -- This provides the IoLogging and FilteredLogging effects. -- -- See also runLogs. withLogging :: Lifted IO e => LogWriter -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Handles the Logs and LogWriterReader effects, while not -- invoking the LogWriter at all. -- -- There is no way to get log output when this logger is used. -- -- Example: -- --
--   exampleWithSomeLogging :: ()
--   exampleWithSomeLogging =
--       run
--     $ withoutLogging
--     $ logDebug "Oh, hi there" -- Nothing written
--   
-- -- This provides the FilteredLogging effect. -- -- See also runLogsWithoutLogging. withoutLogging :: Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withLogging. runLogs :: forall e b. (Member LogWriterReader (Logs : e), Lifted IO e) => LogPredicate -> Eff (Logs : e) b -> Eff e b -- | 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 message with alertSeverity. logAlert :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a criticalSeverity message. logCritical :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a errorSeverity message. logError :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a warningSeverity message. logWarning :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a noticeSeverity message. logNotice :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a informationalSeverity message. logInfo :: forall e. (HasCallStack, Member Logs e) => Text -> Eff e () -- | Log a debugSeverity message. logDebug :: 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) => String -> 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) => String -> 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) => String -> 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) => String -> Eff e () -- | Log the current callStack using the given Severity. logCallStack :: forall e. (HasCallStack, Member Logs e) => Severity -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multi line log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. logMultiLine :: forall e. (HasCallStack, Member Logs e) => Severity -> [Text] -> Eff e () -- | Issue a log statement for each item in the list prefixed with a line -- number and a message hash. -- -- When several concurrent processes issue log statements, multiline log -- statements are often interleaved. -- -- In order to make the logs easier to read, this function will count the -- items and calculate a unique hash and prefix each message, so a user -- can grep to get all the lines of an interleaved, multi-line log -- message. -- -- This function takes a list of Strings as opposed to -- logMultiLine. logMultiLine' :: forall e. (HasCallStack, Member Logs e) => Severity -> [String] -> Eff e () -- | Get the current Logs filter/transformer function. -- -- See Control.Eff.Log#LogPredicate askLogPredicate :: forall e. Member Logs e => Eff e LogPredicate -- | Keep only those messages, for which a predicate holds. -- -- E.g. to keep only messages which begin with OMG: -- --
--   exampleSetLogWriter :: IO Int
--   exampleSetLogWriter =
--       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 -- | Include LogMessages that match a LogPredicate. -- -- includeLogMessages 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 -- | 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 -- | Change the current LogWriter. modifyLogWriter :: IoLogging e => (LogWriter -> LogWriter) -> Eff e a -> Eff e a -- | Replace the current LogWriter. To add an additional log message -- consumer use addLogWriter setLogWriter :: IoLogging e => LogWriter -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriter censorLogs :: IoLogging 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 . -- mappingLogWriterIO censorLogsIo :: IoLogging e => (LogMessage -> IO LogMessage) -> 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 :: IoLogging e => LogWriter -> Eff e a -> Eff e a -- | 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 -> !Interrupt 'NoRecovery -> !ProcessId -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !Interrupt 'NoRecovery [downProcess] :: ProcessDown -> !ProcessId -- | 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 -- | Exceptions containing Interrupts. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc (Interrupt 'Recoverable) -- | A constraint for an effect set that requires the presence of -- SafeProcesses. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Process [e(N+1) .. e(N+k)], e(N+1) .. e(N+k)] -- -- It constrains e to support the (only) Process effect. -- -- This is more relaxed that HasProcesses since it does not -- require Interrupts. type HasSafeProcesses e inner = (SetMember Process (Process inner) e) -- | Cons Process onto a list of effects. This is called -- SafeProcesses because the the actions cannot be interrupted -- in. type SafeProcesses r = Process r : r -- | A constraint for an effect set that requires the presence of -- Processes. -- -- This constrains the effect list to look like this: [e1 ... eN, -- Interrupts, Process [e(N+1) .. e(N+k)], e(N+1) .. -- e(N+k)] -- -- It constrains e beyond HasSafeProcesses to encompass -- Interrupts. type HasProcesses e inner = (HasSafeProcesses e inner, Member Interrupts e) -- | This adds a layer of the Interrupts effect on top of -- Processes type Processes e = Interrupts : SafeProcesses e -- | 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 -- | Extension of ExitNormally with a custom reason [NormalExitRequestedWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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, see Link for a discussion on linking. [LinkedProcessCrashed] :: ProcessId -> Interrupt 'Recoverable -- | An exit reason that has an error message and is Recoverable. [ErrorInterrupt] :: String -> Interrupt 'Recoverable -- | An interrupt with a custom message. [InterruptedBy] :: forall a. (Typeable a, Show a, NFData a) => a -> Interrupt 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: Interrupt 'NoRecovery -- | A process function returned or exited without any error, and with a -- custom message [ExitNormallyWith] :: forall a. (Typeable a, Show a, NFData a) => a -> 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 -- recovered. [ExitUnhandledError] :: Text -> Interrupt 'NoRecovery -- | A process shall exit immediately, without any cleanup was cancelled -- (e.g. killed, in cancel) [ExitProcessCancelled] :: Maybe ProcessId -> Interrupt 'NoRecovery -- | A process that is vital to the crashed process was not running [ExitOtherProcessNotRunning] :: ProcessId -> 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 -- | 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 -- | Serialize a message into a StrictDynamic value to be -- sent via sendAnyMessage. -- -- This indirection allows, among other things, the composition of -- Servers. newtype Serializer message MkSerializer :: (message -> StrictDynamic) -> Serializer message [runSerializer] :: Serializer message -> message -> StrictDynamic -- | 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 -- | A number of micro seconds. newtype Timeout TimeoutMicros :: Int -> Timeout [fromTimeoutMicros] :: Timeout -> Int -- | A multi-line text describing the current state of a process for -- debugging purposes. newtype ProcessDetails MkProcessDetails :: Text -> ProcessDetails [_fromProcessDetails] :: ProcessDetails -> Text -- | A short title for a Process for logging purposes. newtype ProcessTitle MkProcessTitle :: Text -> ProcessTitle [_fromProcessTitle] :: ProcessTitle -> Text -- | 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 ()) -- | Simply wait until the time in the given Timeout has elapsed and -- return. [Delay] :: Timeout -> 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] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: ProcessTitle -> Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | 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. -- -- You might wonder: Why not tearing down the linked process when exiting -- normally? I thought about this. If a process exits normally, it should -- have the opportunity to shutdown stuff explicitly. And if you want to -- make sure that there are no dangling child processes after e.g. a -- broker crash, you can always use monitor. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Update the ProcessDetails of a process [UpdateProcessDetails] :: ProcessDetails -> Process r (ResumeProcess ()) -- | Get the ProcessState (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe (ProcessTitle, ProcessDetails, ProcessState))) -- | An isomorphism lens for the ProcessTitle fromProcessTitle :: Lens' ProcessTitle Text -- | An isomorphism lens for the ProcessDetails fromProcessDetails :: Lens' ProcessDetails Text -- | 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 Processes by wrapping them -- up in interruptToExit and then do a process Shutdown. provideInterruptsShutdown :: forall e a. Eff (Processes e) a -> Eff (SafeProcesses 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, HasProcesses r q) => 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasProcesses r q, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Use executeAndResumeOrExit to execute YieldProcess. -- Refer to YieldProcess for more information. yieldProcess :: forall r q. (HasProcesses r q, HasCallStack) => Eff r () -- | Simply block until the time in the Timeout has passed. delay :: forall r q. (HasProcesses r q, HasCallStack) => Timeout -> 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 o r q. (HasProcesses r q, HasCallStack, 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. (HasCallStack, HasProcesses r q) => 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. (HasCallStack, HasProcesses r q) => 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. (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes q) () -> Eff r () -- | Start a new process, and immediately link to it. -- -- See Link for a discussion on linking. spawnLink :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (Processes 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 SafeProcesses effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Eff (SafeProcesses q) () -> Eff r () -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r Bool -- | Return the ProcessTitle, ProcessDetails and -- ProcessState, for the given process, if the process is alive. getProcessState :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r (Maybe (ProcessTitle, ProcessDetails, ProcessState)) -- | Replace the ProcessDetails of the process. updateProcessDetails :: forall r q. (HasCallStack, HasProcesses r q) => ProcessDetails -> Eff r () -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => 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, HasProcesses r q) => Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, HasProcesses r q) => 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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. (HasSafeProcesses r q, 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, HasSafeProcesses r q) => Eff r ProcessId -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, HasProcesses r q) => 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, HasProcesses r q) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, HasProcesses r q) => MonitorReference -> Eff r () -- | monitor another process before while performing an action and -- demonitor afterwards. withMonitor :: (HasCallStack, HasProcesses r q) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, HasProcesses r q, 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. -- -- The parameter is the value obtained by monitor. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | A MessageSelector for the ProcessDown message. of a -- specific process. -- -- In contrast to selectProcessDown this function matches the -- ProcessId. selectProcessDownByProcessId :: ProcessId -> 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. -- -- See Link for a discussion on linking. linkProcess :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. -- -- See Link for a discussion on linking. unlinkProcess :: forall r q. (HasCallStack, HasProcesses r q) => ProcessId -> Eff r () -- | Exit the process with a Interrupt. exitBecause :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Interrupt 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, HasSafeProcesses r q) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, HasSafeProcesses r q) => String -> Eff r a -- | A ProcessId and a Serializer. EXPERIMENTAL -- -- See sendToReceiver. data Receiver a Receiver :: ProcessId -> (a -> out) -> Receiver a [_receiverPid] :: Receiver a -> ProcessId [_receiverSerializer] :: Receiver a -> a -> out fromProcessId :: Iso' ProcessId Int -- | Serialize and send a message to the process in a Receiver. -- -- EXPERIMENTAL sendToReceiver :: (NFData o, HasProcesses r q) => Receiver o -> o -> Eff r () receiverPid :: forall a_aHaF. Lens' (Receiver a_aHaF) ProcessId -- | A class for Pdu instances that embed other Pdu. -- -- This is a part of Embeds provide instances for your Pdus -- but in client code use the Embeds constraint. -- -- Instances of this class serve as proof to Embeds that a -- conversion into another Pdu actually exists. -- -- A Prism for the embedded Pdu is the center of this class -- -- Laws: embeddedPdu = prism' embedPdu fromPdu class (Typeable protocol, Typeable embeddedProtocol) => HasPduPrism protocol embeddedProtocol -- | A Prism for the embedded Pdus. embeddedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Prism' (Pdu protocol result) (Pdu embeddedProtocol result) -- | Embed the Pdu value of an embedded protocol into the -- corresponding Pdu value. embedPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu embeddedProtocol result -> Pdu protocol result -- | Examine a Pdu value from the outer protocol, and return it, if -- it embeds a Pdu of embedded protocol, otherwise return -- Nothing/ fromPdu :: forall (result :: Synchronicity). HasPduPrism protocol embeddedProtocol => Pdu protocol result -> Maybe (Pdu embeddedProtocol result) -- | This type function takes an Pdu and analysis the reply type, -- i.e. the Synchronicity and evaluates to either t for -- an Pdu x (Synchronous t) or to '()' for an Pdu x -- Asynchronous. type family ProtocolReply (s :: Synchronicity) -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Pdu -- 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 Constraint that bundles the requirements for the Pdu -- values of a protocol. -- -- This ensures that Pdus can be strictly and deeply evaluated and -- shown such that for example logging is possible. type TangiblePdu p r = (Typeable p, Typeable r, Tangible (Pdu p r), HasPdu p) -- | 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. type Tangible i = (NFData i, Typeable i, Show i) -- | A constraint that requires that the outer Pdu has a -- clause to embed values from the inner Pdu. -- -- Also, this constraint requires a HasPduPrism instance, as a -- proof for a possible conversion of an embedded Pdu value into -- to the enclosing Pdu. -- -- This generates better compiler error messages, when an embedding of a -- Pdu into another. -- -- This is provided by HasPdu instances. The instances are -- required to provide a list of embedded Pdu values in -- EmbeddedPduList. -- -- Note that every type embeds itself, so Embeds x x always -- holds. type Embeds outer inner = (HasPduPrism outer inner, CheckEmbeds outer inner, HasPdu outer) -- | This type class and the associated data family defines the protocol -- data units (PDU) of a protocol. -- -- A Protocol in the sense of a communication interface description -- between processes. -- -- The first parameter is usually a user defined type that identifies the -- protocol that uses the Pdus are. It maybe a phantom -- type. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Pdu instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   instance Typeable r => HasPdu BookShop r where
--     data instance Pdu BookShop r where
--       RentBook  :: BookId   -> Pdu BookShop ('Synchronous (Either RentalError RentalId))
--       BringBack :: RentalId -> Pdu BookShop 'Asynchronous
--       deriving Typeable
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
class Typeable protocol => HasPdu (protocol :: Type) where { -- | A type level list Protocol phantom types included in the associated -- Pdu instance. -- -- This is just a helper for better compiler error messages. It relies on -- Embeds to add the constraint HasPduPrism. type family EmbeddedPduList protocol :: [Type]; -- | The protocol data unit type for the given protocol. data family Pdu protocol (reply :: Synchronicity); type EmbeddedPduList protocol = '[]; } -- | A server process for protocol. -- -- Protocols are represented by phantom types, which are used in -- different places to index type families and type class instances. -- -- A Process can send and receive any messages. An Endpoint -- wraps around a ProcessId and carries a phantom type to indicate -- the kinds of messages accepted by the process. -- -- As a metaphor, communication between processes can be thought of -- waiting for and sending protocol data units belonging to some -- protocol. newtype Endpoint protocol Endpoint :: ProcessId -> Endpoint protocol [_fromEndpoint] :: Endpoint protocol -> ProcessId -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API proxyAsEndpoint :: proxy protocol -> ProcessId -> Endpoint protocol -- | Tag a ProcessId with an Pdu type index to mark it a -- Endpoint process handling that API asEndpoint :: forall protocol. ProcessId -> Endpoint protocol -- | Convert an Endpoint to an endpoint for an embedded protocol. -- -- See Embeds, fromEmbeddedEndpoint. toEmbeddedEndpoint :: forall inner outer. Embeds outer inner => Endpoint outer -> Endpoint inner -- | Convert an Endpoint to an endpoint for a server, that embeds -- the protocol. -- -- See Embeds, toEmbeddedEndpoint. fromEmbeddedEndpoint :: forall outer inner. HasPduPrism outer inner => Endpoint inner -> Endpoint outer fromEndpoint :: forall protocol_aUDz protocol_aVLX. Iso (Endpoint protocol_aUDz) (Endpoint protocol_aVLX) ProcessId ProcessId -- | Target of a Call reply. -- -- This combines a RequestOrigin with a Serializer for a -- Reply using Arg. There are to smart constructors for -- this type: replyTarget and embeddedReplyTarget. -- -- Because of Arg the Eq and Ord instances are -- implemented via the RequestOrigin instances. newtype ReplyTarget p r MkReplyTarget :: Arg (RequestOrigin p r) (Serializer (Reply p r)) -> ReplyTarget p r -- | Wraps the source ProcessId and a unique identifier for a -- Call. data RequestOrigin (proto :: Type) reply RequestOrigin :: !ProcessId -> !Int -> RequestOrigin reply [_requestOriginPid] :: RequestOrigin reply -> !ProcessId [_requestOriginCallRef] :: RequestOrigin reply -> !Int -- | The wrapper around replies to Calls. data Reply protocol reply [Reply] :: Tangible reply => {_replyTo :: RequestOrigin protocol reply, _replyValue :: reply} -> Reply protocol reply -- | A wrapper sum type for calls and casts for the Pdus of a -- protocol data Request protocol [Call] :: forall protocol reply. (Tangible reply, TangiblePdu protocol ( 'Synchronous reply)) => RequestOrigin protocol reply -> Pdu protocol ( 'Synchronous reply) -> Request protocol [Cast] :: forall protocol. (TangiblePdu protocol 'Asynchronous, NFData (Pdu protocol 'Asynchronous)) => Pdu protocol 'Asynchronous -> Request protocol -- | Create a new, unique RequestOrigin value for the current -- process. makeRequestOrigin :: (Typeable r, NFData r, HasProcesses e q0) => Eff e (RequestOrigin p r) -- | Turn an RequestOrigin to an origin for an embedded request (See -- Embeds). -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also embedReplySerializer. toEmbeddedOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin outer reply -> RequestOrigin inner reply -- | Turn an embedded RequestOrigin to a RequestOrigin -- for the bigger request. -- -- This is the inverse of toEmbeddedOrigin. -- -- This function is strict in all parameters. embedRequestOrigin :: forall outer inner reply. Embeds outer inner => RequestOrigin inner reply -> RequestOrigin outer reply -- | Turn a Serializer for a Pdu instance that contains -- embedded Pdu values into a Reply Serializer for -- the embedded Pdu. -- -- This is useful of a server delegates the calls and -- casts for an embedded protocol to functions, that require the -- Serializer and RequestOrigin in order to call -- sendReply. -- -- See also toEmbeddedOrigin. embedReplySerializer :: forall outer inner reply. Embeds outer inner => Serializer (Reply outer reply) -> Serializer (Reply inner reply) -- | Answer a Call by sending the reply value to the client process. -- -- The ProcessId, the RequestOrigin and the Reply -- Serializer are stored in the ReplyTarget. sendReply :: (HasProcesses eff q, Tangible reply, Typeable protocol) => ReplyTarget protocol reply -> reply -> Eff eff () -- | Smart constructor for a ReplyTarget. -- -- To build a ReplyTarget for an Embeds instance use -- embeddedReplyTarget. replyTarget :: Serializer (Reply p reply) -> RequestOrigin p reply -> ReplyTarget p reply -- | A simple Lens for the RequestOrigin of a -- ReplyTarget. replyTargetOrigin :: Lens' (ReplyTarget p reply) (RequestOrigin p reply) -- | A simple Lens for the Reply Serializer of a -- ReplyTarget. replyTargetSerializer :: Lens' (ReplyTarget p reply) (Serializer (Reply p reply)) -- | Smart constructor for an embedded ReplyTarget. -- -- This combines replyTarget and toEmbeddedReplyTarget. embeddedReplyTarget :: Embeds outer inner => Serializer (Reply outer reply) -> RequestOrigin outer reply -> ReplyTarget inner reply -- | Convert a ReplyTarget to be usable for embedded replies. -- -- This combines a toEmbeddedOrigin with -- embedReplySerializer to produce a ReplyTarget that can -- be passed to functions defined soley on an embedded protocol. toEmbeddedReplyTarget :: Embeds outer inner => ReplyTarget outer reply -> ReplyTarget inner reply -- | 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 -- | 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. (HasCallStack, HasProcesses r q, 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. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitor combined with -- receiveSelectedAfter. receiveSelectedWithMonitorAfter :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> Eff r (Either (Either ProcessDown TimerElapsed) a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfterWithTitle. receiveAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Typeable a, NFData a, Show a) => Timeout -> ProcessTitle -> 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 startTimerWithTitle. receiveSelectedAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => MessageSelector a -> Timeout -> ProcessTitle -> Eff r (Either TimerElapsed a) -- | Like receiveWithMonitorWithTitle combined with -- receiveSelectedAfterWithTitle. receiveSelectedWithMonitorAfterWithTitle :: forall a r q. (HasCallStack, HasProcesses r q, Show a, Typeable a) => ProcessId -> MessageSelector a -> Timeout -> ProcessTitle -> 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. (HasCallStack, HasProcesses r q, Typeable message, NFData message) => ProcessId -> Timeout -> (TimerReference -> message) -> Eff r TimerReference -- | Like sendAfter but with a user provided name for the timer -- process. sendAfterWithTitle :: forall r q message. (HasCallStack, HasProcesses r q, Typeable message, NFData message) => ProcessTitle -> 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. -- -- This calls sendAfterWithTitle under the hood with -- TimerElapsed as message. startTimerWithTitle :: forall r q. (HasCallStack, HasProcesses r q) => ProcessTitle -> Timeout -> 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. -- -- Calls sendAfter under the hood. startTimer :: forall r q. (HasCallStack, HasProcesses r q) => Timeout -> Eff r TimerReference -- | Cancel a timer started with startTimer. cancelTimer :: forall r q. (HasCallStack, HasProcesses r q) => TimerReference -> Eff r () -- | The reader effect for ProcessIds for Pdus, see -- runEndpointReader type EndpointReader o = Reader (Endpoint o) -- | Instead of passing around a Endpoint value and passing to -- functions like cast or call, a Endpoint can -- provided by a Reader effect, if there is only a single -- server for a given Pdu instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Endpoint. type HasEndpointReader o r = (Typeable o, Member (EndpointReader o) r) -- | Send a request Pdu that has no reply and return immediately. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasCallStack, HasProcesses r q, HasPdu destination, HasPdu protocol, Tangible (Pdu destination 'Asynchronous), Embeds destination protocol) => Endpoint destination -> Pdu protocol 'Asynchronous -> Eff r () -- | Send a request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu clause -- is Synchronous. -- -- Always prefer callWithTimeout over call call :: forall result destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Embeds destination protocol, HasCallStack) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Eff r result -- | Send an request Pdu and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Pdu 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 destination protocol r q. (HasProcesses r q, TangiblePdu destination ( 'Synchronous result), TangiblePdu protocol ( 'Synchronous result), Tangible result, Member Logs r, HasCallStack, Embeds destination protocol) => Endpoint destination -> Pdu protocol ( 'Synchronous result) -> Timeout -> Eff r result -- | Run a reader effect that contains the one server handling a -- specific Pdu instance. runEndpointReader :: HasCallStack => Endpoint o -> Eff (EndpointReader o : r) a -> Eff r a -- | Get the Endpoint registered with runEndpointReader. askEndpoint :: Member (EndpointReader o) e => Eff e (Endpoint o) -- | Like call but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use callSingleton. callEndpointReader :: forall reply o r q. (HasEndpointReader o r, HasCallStack, Tangible reply, TangiblePdu o ( 'Synchronous reply), HasProcesses r q, Embeds o o) => Pdu o ( 'Synchronous reply) -> Eff r reply -- | Like cast but take the Endpoint from the reader provided -- by runEndpointReader. -- -- When working with an embedded Pdu use castSingleton. castEndpointReader :: forall o r q. (HasEndpointReader o r, HasProcesses r q, Tangible (Pdu o 'Asynchronous), HasCallStack, HasPdu o, Embeds o o) => Pdu o 'Asynchronous -> Eff r () -- | Like callEndpointReader, uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- callEndpointReader. callSingleton :: forall outer inner reply q e. (HasCallStack, Member (EndpointReader outer) e, Embeds outer inner, Embeds outer outer, HasProcesses e q, TangiblePdu outer ( 'Synchronous reply), TangiblePdu inner ( 'Synchronous reply), Tangible reply) => Pdu inner ( 'Synchronous reply) -> Eff e reply -- | Like castEndpointReader, but uses embedPdu to embed the -- value. -- -- This function makes use of AmbigousTypes and TypeApplications. -- -- When not working with an embedded Pdu use -- castEndpointReader. castSingleton :: forall outer inner q e. (HasCallStack, Member (EndpointReader outer) e, Tangible (Pdu outer 'Asynchronous), HasProcesses e q, HasPdu outer, HasPdu inner, Embeds outer inner, Embeds outer outer) => Pdu inner 'Asynchronous -> Eff e () -- | Alias for the effect that contains the observers managed by -- evalObserverRegistryState type ObserverRegistryState event = State (ObserverRegistry event) -- | A protocol for managing Observers, encompassing registration -- and de-registration of Observers. data ObserverRegistry (event :: Type) MkObserverRegistry :: Map ProcessId (ObservationSink event) -> ObserverRegistry [_observerRegistry] :: ObserverRegistry -> Map ProcessId (ObservationSink event) -- | Convenience type alias. type CanObserve eventSink event = (Tangible event, Embeds eventSink (Observer event), HasPdu eventSink) -- | Convenience type alias. type IsObservable eventSource event = (Tangible event, Embeds eventSource (ObserverRegistry event), HasPdu eventSource) -- | The Information necessary to wrap an Observed event to a -- process specific message, e.g. the embedded Observer Pdu -- instance, and the MonitorReference of the destination process. data ObservationSink event -- | A protocol to communicate Observed events from a sources -- to many sinks. -- -- A sink is any process that serves a protocol with a Pdu -- instance that embeds the Observer Pdu via an HasPduPrism -- instance. -- -- This type has dual use, for one it serves as type-index for -- Pdu, i.e. HasPdu respectively, and secondly it contains -- an ObservationSink and a MonitorReference. -- -- The ObservationSink is used to serialize and send the -- Observed events, while the ProcessId serves as key for -- internal maps. newtype Observer event MkObserver :: Arg ProcessId (ObservationSink event) -> Observer event -- | And an Observer to the set of recipients for all observations -- reported by observerRegistryNotify. Note that the -- observerRegistry 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 :: forall event eventSink eventSource r q. (HasCallStack, HasProcesses r q, IsObservable eventSource event, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message forgetObserver :: forall event eventSink eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), Tangible (Pdu eventSink 'Asynchronous), IsObservable eventSource event, CanObserve eventSink event) => Endpoint eventSource -> Endpoint eventSink -> Eff r () -- | Send the ForgetObserver message, use a raw ProcessId as -- parameter. forgetObserverUnsafe :: forall event eventSource r q. (HasProcesses r q, HasCallStack, Tangible (Pdu eventSource 'Asynchronous), IsObservable eventSource event) => Endpoint eventSource -> ProcessId -> Eff r () -- | Provide the implementation for the ObserverRegistry Protocol, -- this handled RegisterObserver and ForgetObserver -- messages. It also adds the ObserverRegistryState constraint to -- the effect list. observerRegistryHandlePdu :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => Pdu (ObserverRegistry event) 'Asynchronous -> Eff r () -- | Remove the entry in the ObserverRegistry for the -- ProcessId and return True if there was an entry, -- False otherwise. observerRegistryRemoveProcess :: forall event q r. (HasCallStack, Typeable event, HasProcesses r q, Member (ObserverRegistryState event) r, Member Logs r) => ProcessId -> Eff r Bool -- | Keep track of registered Observers. -- -- Handle the ObserverRegistryState effect, i.e. run -- evalState on an emptyObserverRegistry. evalObserverRegistryState :: HasCallStack => Eff (ObserverRegistryState event : r) a -> Eff r a -- | The empty ObserverRegistryState emptyObserverRegistry :: ObserverRegistry event -- | Report an observation to all observers. The process needs to -- evalObserverRegistryState and to -- observerRegistryHandlePdu. observerRegistryNotify :: forall event r q. (HasProcesses r q, Member (ObserverRegistryState event) r, Tangible event, HasCallStack) => event -> Eff r () -- | Enable logging to IO using the richLogWriter. -- -- Example: -- --
--   exampleWithIoLogging :: IO ()
--   exampleWithIoLogging =
--       runLift
--     $ withRichLogging debugTraceLogWriter
--                     "my-app"
--                     local7
--                     (lmSeverityIsAtLeast informationalSeverity)
--     $ logInfo "Oh, hi there"
--   
withRichLogging :: Lifted IO e => LogWriter -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Decorate an IO based LogWriter to fill out these fields in -- LogMessages: -- -- -- -- It works by using mappingLogWriterIO. richLogWriter :: Text -> Facility -> LogWriter -> LogWriter -- | Enable logging to a file, with some LogMessage fields preset as -- described in withRichLogging. -- -- 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 renderLogMessageConsoleLog
--     $ logInfo "Oh, hi there"
--   
-- -- To vary the LogWriter use withRichLogging. withFileLogging :: (Lifted IO e, MonadBaseControl IO (Eff e), HasCallStack) => FilePath -> Text -> Facility -> LogPredicate -> LogMessageTextRenderer -> Eff (Logs : (LogWriterReader : 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
--     $ withoutLogging
--     $ withFileLogWriter "test.log" renderLogMessageConsoleLog
--     $ logInfo "Oh, hi there"
--   
withFileLogWriter :: (IoLogging e, MonadBaseControl IO (Eff e), HasCallStack) => FilePath -> LogMessageTextRenderer -> Eff e b -> Eff e b -- | Enable logging via traceM using the debugTraceLogWriter, -- with some LogMessage fields preset as in -- withRichLogging. -- -- 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 : e)) a -> Eff e a -- | 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
--     $ withoutLogging @IO
--     $ withTraceLogWriter
--     $ logInfo "Oh, hi there"
--   
withTraceLogWriter :: IoLogging e => Eff e a -> Eff e a -- | Write LogMessages via traceM. debugTraceLogWriter :: LogMessageTextRenderer -> LogWriter -- | Enable logging to standard output using the -- consoleLogWriter, with some LogMessage fields preset as -- in withRichLogging. -- -- 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 withRichLogging. withConsoleLogging :: Lifted IO e => Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Enable logging to standard output using the -- consoleLogWriter. -- -- Log messages are rendered using renderLogMessageConsoleLog. -- -- Example: -- --
--   exampleWithConsoleLogWriter :: IO ()
--   exampleWithConsoleLogWriter =
--       runLift
--     $ withoutLogging @IO
--     $ withConsoleLogWriter
--     $ logInfo "Oh, hi there"
--   
withConsoleLogWriter :: IoLogging e => Eff e a -> Eff e a -- | Render a LogMessage to stdout using -- renderLogMessageConsoleLog. -- -- See stdoutLogWriter. consoleLogWriter :: IO LogWriter -- | This is a wrapper around withAsyncLogWriter and -- withRichLogging. -- -- 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 -> len -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | 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 :: (IoLogging e, MonadBaseControl IO (Eff e), Integral len) => len -> Eff e a -> Eff e a -- | Enable logging to a remote host via UDP, with some -- LogMessage fields preset as in withRichLogging. -- -- See exampleUdpRFC3164Logging withUDPLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => (LogMessage -> Text) -> String -> String -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Enable logging to a (remote-) host via UDP. -- -- See exampleUdpRFC3164Logging withUDPLogWriter :: (IoLogging e, MonadBaseControl IO (Eff e), HasCallStack) => (LogMessage -> Text) -> String -> String -> Eff e b -> Eff e b -- | Enable logging to a unix domain socket, with some -- LogMessage fields preset as in withRichLogging. -- -- See exampleDevLogSyslogLogging withUnixSocketLogging :: (HasCallStack, MonadBaseControl IO (Eff e), Lifted IO e) => LogMessageRenderer Text -> FilePath -> Text -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader : e)) a -> Eff e a -- | Enable logging to a (remote-) host via UnixSocket. -- -- See exampleDevLogSyslogLogging withUnixSocketLogWriter :: (IoLogging e, MonadBaseControl IO (Eff e), HasCallStack) => LogMessageRenderer Text -> FilePath -> Eff e b -> Eff e b -- | 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 () -- | Run the Effects using a single threaded, coroutine based, pure -- scheduler from -- Control.Eff.Concurrent.Process.SingleThreadedScheduler. schedule :: Eff Effects a -> Either (Interrupt 'NoRecovery) a -- | The effect list for Process effects in the single threaded pure -- scheduler. -- -- See PureEffects type Effects = PureEffects -- | The effect list for Process effects in the single threaded pure -- scheduler. This is like SafeProcesses, no Interrupts are -- present. -- -- See PureSafeEffects type SafeEffects = PureSafeEffects -- | The effect list for the underlying scheduler. type BaseEffects = PureBaseEffects -- | Constraint for the existence of the underlying scheduler effects. type HasBaseEffects e = HasPureBaseEffects e