-- 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.19.0 -- | Add-ons to Exception and Exception module Control.Eff.ExceptionExtra -- | Catch Exception thrown by an effect. liftTry :: forall e r a. (HasCallStack, Exception e, Lifted IO r) => Eff r a -> Eff r (Either e a) -- | Very similar to liftEither but for Maybes. Unlike -- liftMaybe this will throw the given value (instead of using -- Fail). maybeThrow :: Member (Exc x) e => x -> Maybe a -> Eff e a instance Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Reader.Lazy.Reader x : e)) instance Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Reader.Lazy.Reader x : e)) instance Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Reader.Lazy.Reader x : e)) instance Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Reader.Strict.Reader x : e)) instance Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Reader.Strict.Reader x : e)) instance Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Reader.Strict.Reader x : e)) instance Control.Monad.Catch.MonadThrow m => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff '[Control.Eff.Internal.Lift m]) instance Control.Monad.Catch.MonadCatch m => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff '[Control.Eff.Internal.Lift m]) instance Control.Monad.Catch.MonadMask m => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff '[Control.Eff.Internal.Lift m]) instance Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Exception.Exc x : e)) instance Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Exception.Exc x : e)) instance Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Exception.Exc x : e)) -- | An RFC 5434 inspired log message and convenience functions for logging -- them. module Control.Eff.Log.Message -- | A message data type inspired by the RFC-5424 Syslog Protocol data LogMessage MkLogMessage :: !Facility -> !Severity -> !Maybe UTCTime -> !Maybe String -> !Maybe String -> !Maybe String -> !Maybe String -> ![StructuredDataElement] -> !Maybe ThreadId -> !Maybe SrcLoc -> !String -> LogMessage [_lmFacility] :: LogMessage -> !Facility [_lmSeverity] :: LogMessage -> !Severity [_lmTimestamp] :: LogMessage -> !Maybe UTCTime [_lmHostname] :: LogMessage -> !Maybe String [_lmAppName] :: LogMessage -> !Maybe String [_lmProcessId] :: LogMessage -> !Maybe String [_lmMessageId] :: LogMessage -> !Maybe String [_lmStructuredData] :: LogMessage -> ![StructuredDataElement] [_lmThreadId] :: LogMessage -> !Maybe ThreadId [_lmSrcLoc] :: LogMessage -> !Maybe SrcLoc [_lmMessage] :: LogMessage -> !String -- | 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 String -> f (Maybe String)) -> 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 is implemented in discriminateByAppName. lmAppName :: Functor f => (Maybe String -> f (Maybe String)) -> LogMessage -> f LogMessage -- | A lens for a user defined of process id of a LogMessage lmProcessId :: Functor f => (Maybe String -> f (Maybe String)) -> LogMessage -> f LogMessage -- | A lens for a user defined message id of a LogMessage lmMessageId :: Functor f => (Maybe String -> f (Maybe String)) -> 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 => (String -> f String) -> LogMessage -> f LogMessage -- | Put the source location of the given callstack in lmSrcLoc setCallStack :: CallStack -> LogMessage -> LogMessage -- | Prefix the lmMessage. prefixLogMessagesWith :: String -> 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 -- | Render a LogMessage according to the rules in the given RFC, -- except for the rules concerning unicode and ascii renderRFC5424 :: LogMessage -> String -- | Render a LogMessage but set the timestamp and thread id fields. printLogMessage :: LogMessage -> IO () -- | Render a LogMessage human readable. renderLogMessage :: LogMessage -> String -- | Construct a LogMessage with errorSeverity errorMessage :: HasCallStack => String -> LogMessage -- | Construct a LogMessage with informationalSeverity infoMessage :: HasCallStack => String -> LogMessage -- | Construct a LogMessage with debugSeverity debugMessage :: HasCallStack => String -> 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) => String -> m LogMessage -- | Construct a LogMessage with informationalSeverity infoMessageIO :: (HasCallStack, MonadIO m) => String -> m LogMessage -- | Construct a LogMessage with debugSeverity debugMessageIO :: (HasCallStack, MonadIO m) => String -> m LogMessage -- | The filter predicate for message that shall be logged. -- -- See Control.Eff.Log.Handler#LogPredicate type LogPredicate = LogMessage -> Bool -- | All messages. allLogMessages :: LogPredicate -- | No messages. noLogMessages :: LogPredicate -- | Match LogMessages that have exactly the given severity. See -- lmSeverityIsAtLeast. -- -- See Control.Eff.Log.Handler#LogPredicate lmSeverityIs :: Severity -> LogPredicate -- | Match LogMessages that have the given severity or worse. -- See lmSeverityIs. -- -- See Control.Eff.Log.Handler#LogPredicate lmSeverityIsAtLeast :: Severity -> LogPredicate -- | Match LogMessages whose lmMessage starts with the given -- string. -- -- See Control.Eff.Log.Handler#LogPredicate lmMessageStartsWith :: String -> LogPredicate -- | Apply discriminate LogMessages by their 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. discriminateByAppName :: String -> LogPredicate -> LogPredicate -> LogPredicate -- | RFC-5424 defines how structured data can be included in a log message. data StructuredDataElement SdElement :: !String -> ![SdParameter] -> StructuredDataElement [_sdElementId] :: StructuredDataElement -> !String [_sdElementParameters] :: StructuredDataElement -> ![SdParameter] -- | Component of an RFC-5424 StructuredDataElement data SdParameter MkSdParameter :: !String -> !String -> SdParameter -- | Extract the name of an SdParameter the length is cropped to 32 -- according to RFC 5424. sdName :: String -> String -- | Extract the value of an SdParameter. sdParamValue :: String -> String -- | A lens for the key or ID of a group of RFC 5424 key-value pairs. sdElementId :: Functor f => (String -> f String) -> 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 data Facility -- | 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 GHC.Base.String instance GHC.Show.Show Control.Eff.Log.Message.LogMessage 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.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.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 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 GHC.Show.Show Control.Eff.Log.Message.StructuredDataElement instance Control.DeepSeq.NFData Control.Eff.Log.Message.StructuredDataElement instance GHC.Show.Show Control.Eff.Log.Message.SdParameter instance Control.DeepSeq.NFData Control.Eff.Log.Message.SdParameter -- | The LogWriter type encapsulates an effectful function to write -- LogMessages. -- -- Used in conjunction with the SupportsLogger class, it can be -- used to write messages from within an effectful computation. module Control.Eff.Log.Writer -- | A function that takes a log message and returns an effect that -- logs the message. newtype LogWriter writerM MkLogWriter :: (LogMessage -> writerM ()) -> LogWriter writerM [runLogWriter] :: LogWriter writerM -> LogMessage -> writerM () -- | This class describes how to lift the log writer action into some -- monad. -- -- The second parameter is almost always Eff x so usually the -- method of this class lifts the log writer action into an effect monad. class SupportsLogger h e liftLogWriter :: SupportsLogger h e => LogWriter h -> LogMessage -> Eff e () -- | This LogWriter will discard all messages. -- -- NOTE: This is just an alias for def noOpLogWriter :: Applicative m => LogWriter m -- | A LogWriter that applies renderLogMessage to the log -- message and then traces it using traceM. This LogWriter -- work with any base monad. debugTraceLogWriter :: Monad h => LogWriter h -- | A base monad for all side effect free LogWriter. -- -- This is only required e.g. when logs are only either discarded or -- traced. See debugTraceLogWriter or noOpLogWriter. -- -- This is just a wrapper around Identity and serves as a type -- that has a special SupportsLogger instance. newtype PureLogWriter a MkPureLogWriter :: Identity a -> PureLogWriter a [runPureLogWriter] :: PureLogWriter a -> Identity a -- | A LogWriter monad that provides pure logging by capturing via -- the Writer effect. listLogWriter :: LogWriter CaptureLogs -- | A LogWriter monad that provides pure logging by capturing via -- the Writer effect. newtype CaptureLogs a MkCaptureLogs :: Eff '[CapturedLogsWriter] a -> CaptureLogs a [unCaptureLogs] :: CaptureLogs a -> Eff '[CapturedLogsWriter] a -- | Alias for the Writer that contains the captured -- LogMessages from CaptureLogs. type CapturedLogsWriter = Writer LogMessage -- | Run a Writer for LogMessages. -- -- Such a Writer is needed for CaptureLogs runCapturedLogsWriter :: Eff (CapturedLogsWriter : e) a -> Eff e (a, [LogMessage]) -- | Write LogMessages to standard output, formatted with -- printLogMessage. consoleLogWriter :: LogWriter IO -- | A LogWriter that uses an IO action to write the message. ioHandleLogWriter :: HasCallStack => Handle -> LogWriter IO -- | A LogWriter that applies a predicate to the LogMessage -- and delegates to to the given writer of the predicate is satisfied. filteringLogWriter :: Monad e => LogPredicate -> LogWriter e -> LogWriter e -- | A LogWriter that applies a function to the LogMessage -- and delegates the result to to the given writer. mappingLogWriter :: (LogMessage -> LogMessage) -> LogWriter e -> LogWriter e -- | Like mappingLogWriter allow the function that changes the -- LogMessage to have effects. mappingLogWriterM :: Monad e => (LogMessage -> e LogMessage) -> LogWriter e -> LogWriter e -- | A LogWriter that uses an IO action to write the message. -- -- Example use cases for this function are the consoleLogWriter -- and the ioHandleLogWriter. ioLogWriter :: HasCallStack => (LogMessage -> IO ()) -> LogWriter IO -- | Decorate an IO based LogWriter to set important fields in log -- messages. -- -- ALl log messages are censored to include basic log message -- information: -- -- -- -- It installs the given LogWriter, wrapped using -- mappingLogWriterM. defaultIoLogWriter :: String -> Facility -> LogWriter IO -> LogWriter IO instance GHC.Base.Monad Control.Eff.Log.Writer.CaptureLogs instance GHC.Base.Applicative Control.Eff.Log.Writer.CaptureLogs instance GHC.Base.Functor Control.Eff.Log.Writer.CaptureLogs instance GHC.Base.Monad Control.Eff.Log.Writer.PureLogWriter instance GHC.Base.Applicative Control.Eff.Log.Writer.PureLogWriter instance GHC.Base.Functor Control.Eff.Log.Writer.PureLogWriter instance Data.OpenUnion.Member Control.Eff.Log.Writer.CapturedLogsWriter e => Control.Eff.Log.Writer.SupportsLogger Control.Eff.Log.Writer.CaptureLogs e instance Control.Eff.Log.Writer.SupportsLogger Control.Eff.Log.Writer.PureLogWriter e instance Control.Eff.Internal.Lifted GHC.Types.IO e => Control.Eff.Log.Writer.SupportsLogger GHC.Types.IO e instance GHC.Base.Applicative w => Data.Default.Class.Default (Control.Eff.Log.Writer.LogWriter w) -- | A memory efficient, streaming, logging effect with support for -- efficiently not logging when no logs are required. -- -- Good support for logging to a file or to the network, as well as -- asynchronous logging in another thread. module Control.Eff.Log.Handler -- | Log a message. -- -- All logging goes through this function. -- -- This function is the only place where the LogPredicate is -- applied. -- -- Also, LogMessages are evaluated using deepseq, -- after they pass the LogPredicate. logMsg :: forall e m. (HasCallStack, Member Logs e, ToLogMessage m) => m -> Eff e () -- | Log a String 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) => 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 () -- | Include LogMessages that match a LogPredicate. -- -- excludeLogMessages p allows log message to be logged if p -- m -- -- Although it is enough if the previous predicate holds. See -- excludeLogMessages and modifyLogPredicate. -- -- See Control.Eff.Log.Handler#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.Handler#LogPredicate excludeLogMessages :: forall e a. Member Logs e => LogPredicate -> Eff e a -> Eff e a -- | Keep only those messages, for which a predicate holds. -- -- E.g. to keep only messages which begin with OMG: -- --
--   exampleLogPredicate :: IO Int
--   exampleLogPredicate =
--       runLift
--     $ withLogging consoleLogWriter
--     $ do logMsg "test"
--          setLogPredicate (\ msg -> case view lmMessage msg of
--                                     'O':'M':'G':_ -> True
--                                     _             -> False)
--                            (do logMsg "this message will not be logged"
--                                logMsg "OMG logged"
--                                return 42)
--   
-- -- In order to also delegate to the previous predicate, use -- modifyLogPredicate -- -- See Control.Eff.Log.Handler#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.Handler#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.Handler#LogPredicate askLogPredicate :: forall e. Member Logs e => Eff e LogPredicate -- | A Reader specialized for LogWriters -- -- The existing Reader couldn't be used together with -- SetMember, so this lazy reader was written, specialized to -- reading LogWriter. data LogWriterReader h v -- | Replace the current LogWriter. To add an additional log message -- consumer use addLogWriter setLogWriter :: forall h e a. LogsTo h e => LogWriter h -> Eff e a -> Eff e a -- | Combine the effects of a given LogWriter and the existing one. -- --
--   exampleAddLogWriter :: IO ()
--   exampleAddLogWriter = go >>= putStrLn
--    where go = fmap (unlines . map renderLogMessage . snd)
--                 $  runLift
--                 $  runCapturedLogsWriter
--                 $  withLogging listLogWriter
--                 $  addLogWriter (mappingLogWriter (lmMessage %~ ("CAPTURED "++)) listLogWriter)
--                 $  addLogWriter (filteringLogWriter testPred (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"
--          testPred = view (lmSeverity . to (<= errorSeverity))
--   
addLogWriter :: forall h e a. (HasCallStack, LogsTo h e, Monad h) => LogWriter h -> Eff e a -> Eff e a -- | Open a file and add the LogWriter in the LogWriterReader -- tha appends the log messages to it. withLogFileAppender :: (Lifted IO e, LogsTo IO e, MonadBaseControl IO (Eff e)) => FilePath -> Eff e b -> Eff e b -- | Modify the the LogMessages written in the given sub-expression. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriter censorLogs :: LogsTo h e => (LogMessage -> LogMessage) -> Eff e a -> Eff e a -- | Modify the the LogMessages written in the given sub-expression, -- as in censorLogs but with a effectful function. -- -- Note: This is equivalent to modifyLogWriter . -- mappingLogWriterM censorLogsM :: (LogsTo h e, Monad h) => (LogMessage -> h LogMessage) -> Eff e a -> Eff e a -- | Get the current LogWriter. askLogWriter :: SetMember LogWriterReader (LogWriterReader h) e => Eff e (LogWriter h) -- | Change the current LogWriter. modifyLogWriter :: forall h e a. LogsTo h e => (LogWriter h -> LogWriter h) -> Eff e a -> Eff e a -- | This effect sends LogMessages and is a reader for a -- LogPredicate. -- -- Logs are sent via logMsg; for more informaion about log -- predicates, see Control.Eff.Log.Handler#LogPredicate -- -- This effect is handled via withLogging. data Logs v -- | A constraint alias for effects that requires a LogWriterReader, -- as well as that the contained LogWriterReader has a -- SupportsLogger instance. -- -- The requirements of this constraint are provided by: * -- withStdOutLogging * withIoLogging * withLogging -- * withSomeLogging type LogsTo h e = (Member Logs e, SupportsLogger h e, SetMember LogWriterReader (LogWriterReader h) e) -- | Enable logging to stdout using the defaultIoLogWriter -- in combination with the consoleLogWriter. -- -- Example: -- --
--   exampleWithConsoleLogging :: IO ()
--   exampleWithConsoleLogging =
--       runLift
--     $ withConsoleLogging "my-app" local7 allLogMessages
--     $ logInfo "Oh, hi there"
--   
-- -- To vary the LogWriter use withIoLogging. withConsoleLogging :: SetMember Lift (Lift IO) e => String -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | Enable logging to IO using the defaultIoLogWriter. -- -- To log to the console (standard output), one can use -- withConsoleLogging. -- -- Example: -- --
--   exampleWithIoLogging :: IO ()
--   exampleWithIoLogging =
--       runLift
--     $ withIoLogging consoleLogWriter
--   
-- -- "my-app" local7 (lmSeverityIsAtLeast informationalSeverity) > $ -- logInfo "Oh, hi there" withIoLogging :: SetMember Lift (Lift IO) e => LogWriter IO -> String -> Facility -> LogPredicate -> Eff (Logs : (LogWriterReader IO : e)) a -> Eff e a -- | Handle the Logs and LogWriterReader effects. -- -- It installs the given LogWriter, which determines the -- underlying LogWriter type parameter. -- -- Example: -- --
--   exampleWithLogging :: IO ()
--   exampleWithLogging =
--       runLift
--     $ withLogging consoleLogWriter
--     $ logDebug "Oh, hi there"
--   
withLogging :: forall h e a. (Applicative h, LogsTo h (Logs : (LogWriterReader h : e))) => LogWriter h -> Eff (Logs : (LogWriterReader h : e)) a -> Eff e a -- | Handles the Logs and LogWriterReader effects. -- -- By default it uses the noOpLogWriter, but using -- setLogWriter the LogWriter can be replaced. -- -- This is like withLogging applied to noOpLogWriter -- -- Example: -- --
--   exampleWithSomeLogging :: ()
--   exampleWithSomeLogging =
--       run
--     $ withSomeLogging @PureLogWriter
--     $ logDebug "Oh, hi there"
--   
withSomeLogging :: forall h e a. (Applicative h, LogsTo h (Logs : (LogWriterReader h : e))) => Eff (Logs : (LogWriterReader h : e)) a -> Eff e a -- | The concrete list of Effects for logging with an IO based -- LogWriter, and a LogWriterReader. type LoggingAndIo = '[Logs, LogWriterReader IO, Lift IO] -- | Raw handling of the Logs effect. Exposed for custom extensions, -- if in doubt use withLogging. runLogs :: forall h e b. (LogsTo h (Logs : e), SupportsLogger h (Logs : e)) => LogPredicate -> Eff (Logs : e) b -> Eff e b -- | Consume log messages. -- -- Exposed for custom extensions, if in doubt use withLogging. -- -- Respond to all LogMessages logged from the given action, up to -- any MonadBaseControl liftings. -- -- Note that all logging is done through logMsg and that means -- only messages passing the LogPredicate are received. -- -- The LogMessages are consumed once they are passed to the -- given callback function, previous respondToLogMessage -- invocations further up in the call stack will not get the messages -- anymore. -- -- Use interceptLogMessages if the messages shall be passed any -- previous handler. -- -- NOTE: The effects of this function are lost when using -- MonadBaseControl, MonadMask, MonadCatch and -- MonadThrow. -- -- In contrast the functions based on modifying the LogWriter, -- such as addLogWriter or censorLogs, are save to use in -- combination with the aforementioned liftings. respondToLogMessage :: forall r b. Member Logs r => (LogMessage -> Eff r ()) -> Eff r b -> Eff r b -- | Change the LogMessages using an effectful function. -- -- Exposed for custom extensions, if in doubt use withLogging. -- -- This differs from respondToLogMessage in that the intercepted -- messages will be written either way, albeit in altered form. -- -- NOTE: The effects of this function are lost when using -- MonadBaseControl, MonadMask, MonadCatch and -- MonadThrow. -- -- In contrast the functions based on modifying the LogWriter, -- such as addLogWriter or censorLogs, are save to use in -- combination with the aforementioned liftings. interceptLogMessages :: forall r b. Member Logs r => (LogMessage -> Eff r LogMessage) -> Eff r b -> Eff r b -- | Provide the LogWriter -- -- Exposed for custom extensions, if in doubt use withLogging. runLogWriterReader :: LogWriter h -> Eff (LogWriterReader h : e) a -> Eff e a instance (Control.Monad.Base.MonadBase m m, Control.Eff.Internal.LiftedBase m e, Control.Eff.Log.Writer.SupportsLogger m (Control.Eff.Log.Handler.Logs : e), Data.OpenUnion.SetMember Control.Eff.Log.Handler.LogWriterReader (Control.Eff.Log.Handler.LogWriterReader m) (Control.Eff.Log.Handler.Logs : e)) => Control.Monad.Trans.Control.MonadBaseControl m (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e), Control.Eff.Log.Writer.SupportsLogger m (Control.Eff.Log.Handler.Logs : e), Data.OpenUnion.SetMember Control.Eff.Log.Handler.LogWriterReader (Control.Eff.Log.Handler.LogWriterReader m) (Control.Eff.Log.Handler.Logs : e)) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e), Control.Eff.Log.Writer.SupportsLogger m (Control.Eff.Log.Handler.Logs : e), Data.OpenUnion.SetMember Control.Eff.Log.Handler.LogWriterReader (Control.Eff.Log.Handler.LogWriterReader m) (Control.Eff.Log.Handler.Logs : e)) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.Logs : e)) instance Control.Eff.Internal.Handle (Control.Eff.Log.Handler.LogWriterReader h) e a (Control.Eff.Log.Writer.LogWriter h -> k) instance (Control.Monad.Base.MonadBase m m, Control.Eff.Internal.LiftedBase m r) => Control.Monad.Trans.Control.MonadBaseControl m (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.LogWriterReader h : r)) instance (Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff e)) => Control.Monad.Catch.MonadThrow (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.LogWriterReader h : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff e)) => Control.Monad.Catch.MonadCatch (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.LogWriterReader h : e)) instance (GHC.Base.Applicative m, Control.Eff.Internal.LiftedBase m e, Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff e)) => Control.Monad.Catch.MonadMask (Control.Eff.Internal.Eff (Control.Eff.Log.Handler.LogWriterReader h : 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 [Dynamic]) -- | In cooperative schedulers, this will give processing time to the -- scheduler. Every other operation implicitly serves the same purpose. [YieldProcess] :: Process r (ResumeProcess ()) -- | Return the current ProcessId [SelfPid] :: Process r (ResumeProcess ProcessId) -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. [Spawn] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Get the process state (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe ProcessState)) -- | Shutdown the process; irregardless of the exit reason, this function -- never returns, [Shutdown] :: ExitReason 'NoRecovery -> Process r a -- | Raise an error, that can be handled. [SendShutdown] :: ProcessId -> ExitReason '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 -> InterruptReason -> 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 -> Dynamic -> 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 ExitReason -- LinkedProcessCrashed. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Each process is identified by a single process id, that stays constant -- throughout the life cycle of a process. Also, message sending relies -- on these values to address messages to processes. newtype ProcessId ProcessId :: Int -> ProcessId [_fromProcessId] :: ProcessId -> Int fromProcessId :: Iso' ProcessId Int -- | Cons Process onto a list of effects. type ConsProcess r = Process r : r -- | Every Process action returns it's actual result wrapped in this -- type. It will allow to signal errors as well as pass on normal results -- such as incoming messages. data ResumeProcess v -- | The current operation of the process was interrupted with a -- ExitReason. If isRecoverable holds for the given reason, -- the process may choose to continue. [Interrupted] :: InterruptReason -> ResumeProcess v -- | The process may resume to do work, using the given result. [ResumeWith] :: a -> ResumeProcess a -- | Every function for Process things needs such a proxy value for -- the low-level effect list, i.e. the effects identified by -- r in Process r : r, this might be -- dependent on the scheduler implementation. data SchedulerProxy :: [Type -> Type] -> Type -- | Tell the type checker what effects we have below Process [SchedulerProxy] :: SchedulerProxy q -- | Like SchedulerProxy but shorter [SP] :: SchedulerProxy q -- | Like SP but different [Scheduler] :: SchedulerProxy q -- | Return a SchedulerProxy for a Process effect. thisSchedulerProxy :: Eff (Process r : r) (SchedulerProxy r) -- | The state that a Process is currently in. data ProcessState -- | The process has just been started but not scheduled yet. ProcessBooting :: ProcessState -- | The process yielded it's time slice ProcessIdle :: ProcessState -- | The process is busy with non-blocking ProcessBusy :: ProcessState -- | The process is busy with sending a message ProcessBusySending :: ProcessState -- | The process is busy with killing ProcessBusySendingShutdown :: ProcessState -- | The process is busy with killing ProcessBusySendingInterrupt :: ProcessState -- | The process blocked by a receiveAnyMessage ProcessBusyReceiving :: ProcessState -- | The process blocked by a linkProcess ProcessBusyLinking :: ProcessState -- | The process blocked by a unlinkProcess ProcessBusyUnlinking :: ProcessState -- | The process blocked by a monitor ProcessBusyMonitoring :: ProcessState -- | The process blocked by a demonitor ProcessBusyDemonitoring :: ProcessState -- | The process was interrupted ProcessInterrupted :: ProcessState -- | The process was shutdown or crashed ProcessShuttingDown :: ProcessState -- | Use executeAndResumeOrExit to execute YieldProcess. -- Refer to YieldProcess for more information. yieldProcess :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Eff r () -- | Send a message to a process addressed by the ProcessId. See -- SendMessage. sendMessage :: forall r q o. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o) => ProcessId -> o -> Eff r () -- | Send a Dynamic value to a process addressed by the -- ProcessId. See SendMessage. sendAnyMessage :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> Dynamic -> Eff r () -- | Exit a process addressed by the ProcessId. The process will -- exit, it might do some cleanup, but is ultimately unrecoverable. See -- SendShutdown. sendShutdown :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> ExitReason 'NoRecovery -> Eff r () -- | Interrupts a process addressed by the ProcessId. The process -- might exit, or it may continue. | Like sendInterrupt, but also -- return True iff the process to exit exists. sendInterrupt :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> InterruptReason -> Eff r () -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r Int -- | Receive and cast the message to some Typeable instance. See -- ReceiveSelectedMessage for more documentation. This will wait -- for a message of the return type using receiveSelectedMessage receiveMessage :: forall a r q. (HasCallStack, Typeable a, Show a, SetMember Process (Process q) r, Member Interrupts r) => Eff r a -- | Block until a message was received, that is not Nothing after -- applying a callback to it. See ReceiveSelectedMessage for more -- documentation. receiveSelectedMessage :: forall r q a. (HasCallStack, Show a, SetMember Process (Process q) r, Member Interrupts r) => MessageSelector a -> Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r [Dynamic] -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r Dynamic -- | Like receiveSelectedLoop but refined to casting to a specific -- Typeable using selectMessageLazy. receiveLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack, Typeable a) => (Either InterruptReason 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 ExitReason, otherwise -- the process will be exited with the same reason using -- exitBecause. See also ReceiveSelectedMessage for more -- documentation. receiveSelectedLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => MessageSelector a -> (Either InterruptReason a -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Like receiveSelectedLoop but not selective. See also -- selectAnyMessageLazy, receiveSelectedLoop. receiveAnyLoop :: forall r q endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => (Either InterruptReason Dynamic -> 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 -- fromDynamic. It will also force the result. selectMessage :: (NFData t, Typeable t) => MessageSelector t -- | Create a message selector for a value that can be obtained by -- fromDynamic. It will also force the result. selectMessageLazy :: Typeable t => MessageSelector t -- | Create a message selector for a value that can be obtained by -- fromDynamic with a proxy argument. It will also force -- the result. selectMessageProxy :: forall proxy t. (NFData t, Typeable t) => proxy t -> MessageSelector t -- | Create a message selector for a value that can be obtained by -- fromDynamic with a proxy argument. It will also force -- the result. selectMessageProxyLazy :: forall proxy t. Typeable t => proxy t -> MessageSelector t -- | Create a message selector from a predicate. It will force the -- result. filterMessage :: (Typeable a, NFData a) => (a -> Bool) -> MessageSelector a -- | Create a message selector from a predicate. It will force the -- result. filterMessageLazy :: 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). It will force the result. selectMessageWith :: (Typeable a, NFData b) => (a -> Maybe b) -> MessageSelector b -- | 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). It will force the result. selectMessageWithLazy :: Typeable a => (a -> Maybe b) -> MessageSelector b -- | Create a message selector. It will force the result. selectDynamicMessage :: NFData a => (Dynamic -> Maybe a) -> MessageSelector a -- | Create a message selector. selectDynamicMessageLazy :: (Dynamic -> Maybe a) -> MessageSelector a -- | Create a message selector that will match every message. This is -- lazy because the result is not forceed. selectAnyMessageLazy :: MessageSelector Dynamic -- | Returns the ProcessId of the current process. self :: (HasCallStack, SetMember Process (Process q) r) => Eff r ProcessId -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r Bool -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. If the new -- process is interrupted, the process will Shutdown with the -- InterruptReason wrapped in NotRecovered. For specific -- use cases it might be better to use spawnRaw. spawn :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r () -- | Start a new process, and immediately link to it. spawnLink :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. The spawned -- process has only the raw ConsProcess effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r () -- | Exit the process with a ExitReason. exitBecause :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => ExitReason 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => String -> Eff r a -- | Connect the calling process to another process, such that if one of -- the processes crashes (i.e. isCrash returns True), the -- other is shutdown with the ExitReason -- LinkedProcessCrashed. linkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. unlinkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Monitor another process. When the monitored process exits a -- ProcessDown is sent to the calling process. The return value is -- a unique identifier for that monitor. There can be multiple monitors -- on the same process, and a message for each will be sent. If the -- process is already dead, the ProcessDown message will be sent -- immediately, without exit reason monitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => MonitorReference -> Eff r () -- | A monitored process exited. This message is sent to a process by the -- scheduler, when a process that was monitored died. data ProcessDown ProcessDown :: !MonitorReference -> !SomeExitReason -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !SomeExitReason -- | A MessageSelector for the ProcessDown message of a -- specific process. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | Make an InterruptReason for a ProcessDown message. -- -- For example: doSomething >>= either (interrupt . -- becauseProcessIsDown) return becauseProcessIsDown :: ProcessDown -> InterruptReason -- | A value that contains a unique reference of a process monitoring. data MonitorReference MonitorReference :: Int -> ProcessId -> MonitorReference [monitorIndex] :: MonitorReference -> Int [monitoredProcess] :: MonitorReference -> ProcessId -- | monitor another process before while performing an action and -- demonitor afterwards. withMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r, Typeable a, Show a) => ProcessId -> MessageSelector a -> Eff r (Either ProcessDown a) -- | Handle all InterruptReasons of an InterruptableProcess -- by wrapping them up in NotRecovered and then do a process -- Shutdown. provideInterruptsShutdown :: forall e a. Eff (InterruptableProcess e) a -> Eff (ConsProcess e) a -- | Handle InterruptReasons 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) => (InterruptReason -> Eff r a) -> Eff r a -> Eff r a -- | Like handleInterrupts, but instead of passing the -- InterruptReason to a handler function, Either is -- returned. tryUninterrupted :: (HasCallStack, Member Interrupts r) => Eff r a -> Eff r (Either InterruptReason a) -- | Handle InterruptReasons arising during process operations, e.g. -- when a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. exitOnInterrupt :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r) => Eff r a -> Eff r a -- | Handle interrupts by logging them with logProcessExit and -- otherwise ignoring them. logInterrupts :: forall r. (Member Logs r, HasCallStack, Member Interrupts r) => Eff r () -> Eff r () -- | Handle InterruptReasons 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 InterruptReason a) -- | Wrap all (left) InterruptReasons into NotRecovered and -- return the (right) NoRecovery ExitReasons as is. mergeEitherInterruptAndExitReason :: Either InterruptReason (ExitReason 'NoRecovery) -> ExitReason 'NoRecovery -- | Throw an InterruptReason, can be handled by -- handleInterrupts or exitOnInterrupt or -- provideInterrupts. interrupt :: (HasCallStack, Member Interrupts r) => InterruptReason -> Eff r a -- | Execute a and action and return the result; if the process is -- interrupted by an error or exception, or an explicit shutdown from -- another process, or through a crash of a linked process, i.e. whenever -- the exit reason satisfies isRecoverable, return the exit -- reason. executeAndResume :: forall q r v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r (Either (ExitReason 'Recoverable) v) -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrExit :: forall r q v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrThrow :: forall q r v. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Process q (ResumeProcess v) -> Eff r v -- | A sum-type with reasons for why a process exists the scheduling loop, -- this includes errors, that can occur when scheduling messages. data ExitReason (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) [ProcessFinished] :: ExitReason 'Recoverable -- | A process that should be running was not running. [ProcessNotRunning] :: ProcessId -> ExitReason 'Recoverable -- | A linked process is down [LinkedProcessCrashed] :: ProcessId -> ExitReason 'Recoverable -- | An exit reason that has an error message but isn't Recoverable. [ProcessError] :: String -> ExitReason 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: ExitReason 'NoRecovery -- | An unhandled Recoverable allows NoRecovery. [NotRecovered] :: ExitReason 'Recoverable -> ExitReason 'NoRecovery -- | An unexpected runtime exception was thrown, i.e. an exception derived -- from SomeException [UnexpectedException] :: String -> String -> ExitReason 'NoRecovery -- | A process was cancelled (e.g. killed, in cancel) [Killed] :: ExitReason 'NoRecovery -- | This kind is used to indicate if a ExitReason can be treated -- like a short interrupt which can be handled or ignored. data ExitRecovery Recoverable :: ExitRecovery NoRecovery :: ExitRecovery -- | ExitReasons which are recoverable are interrupts. type InterruptReason = ExitReason 'Recoverable -- | Exceptions containing InterruptReasons. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc InterruptReason -- | This adds a layer of the Interrupts effect on top of -- ConsProcess type InterruptableProcess e = Interrupts : ConsProcess e -- | This value indicates whether a process exited in way consistent with -- the planned behaviour or not. data ExitSeverity NormalExit :: ExitSeverity Crash :: ExitSeverity -- | An existential wrapper around ExitReason data SomeExitReason [SomeExitReason] :: ExitReason x -> SomeExitReason -- | Get the ExitRecovery toExitRecovery :: ExitReason r -> ExitRecovery -- | A predicate for recoverable exit reasons. This predicate defines the -- exit reasons which functions such as executeAndResume isRecoverable :: ExitReason x -> Bool -- | Get the ExitSeverity of a ExitReason. toExitSeverity :: ExitReason e -> ExitSeverity -- | A predicate for linked process crashes. isBecauseDown :: Maybe ProcessId -> ExitReason r -> Bool -- | A predicate for crashes. A crash happens when a process exits -- with an ExitReason other than ExitNormally isCrash :: ExitReason x -> Bool -- | Print a ExitReason to Just a formatted String -- when isCrash is True. This can be useful in combination -- with view patterns, e.g.: -- --
--   logCrash :: ExitReason -> Eff e ()
--   logCrash (toCrashReason -> Just reason) = logError reason
--   logCrash _ = return ()
--   
-- -- Though this can be improved to: -- --
--   logCrash = traverse_ logError . toCrashReason
--   
toCrashReason :: ExitReason x -> Maybe String -- | Partition a SomeExitReason back into either a NoRecovery -- or a Recoverable ExitReason fromSomeExitReason :: SomeExitReason -> Either (ExitReason 'NoRecovery) InterruptReason -- | Log the ExitReasons logProcessExit :: forall e x. (Member Logs e, HasCallStack) => ExitReason x -> Eff e () instance GHC.Show.Show v => GHC.Show.Show (Control.Eff.Concurrent.Process.ResumeProcess v) instance GHC.Generics.Generic1 Control.Eff.Concurrent.Process.ResumeProcess instance GHC.Generics.Generic (Control.Eff.Concurrent.Process.ResumeProcess v) instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessDown instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessDown instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ProcessDown instance GHC.Generics.Generic Control.Eff.Concurrent.Process.MonitorReference instance GHC.Classes.Ord Control.Eff.Concurrent.Process.MonitorReference instance GHC.Classes.Eq Control.Eff.Concurrent.Process.MonitorReference instance GHC.Read.Read Control.Eff.Concurrent.Process.MonitorReference instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessId instance GHC.Real.Real Control.Eff.Concurrent.Process.ProcessId instance GHC.Real.Integral Control.Eff.Concurrent.Process.ProcessId instance GHC.Enum.Enum Control.Eff.Concurrent.Process.ProcessId instance GHC.Num.Num Control.Eff.Concurrent.Process.ProcessId instance GHC.Enum.Bounded Control.Eff.Concurrent.Process.ProcessId instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessId instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessId instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ExitSeverity instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ExitSeverity instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ExitSeverity instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ExitRecovery instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ExitRecovery instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ExitRecovery instance GHC.Generics.Generic Control.Eff.Concurrent.Process.ProcessState instance GHC.Enum.Enum Control.Eff.Concurrent.Process.ProcessState instance GHC.Classes.Eq Control.Eff.Concurrent.Process.ProcessState instance GHC.Classes.Ord Control.Eff.Concurrent.Process.ProcessState instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessState instance GHC.Read.Read Control.Eff.Concurrent.Process.ProcessState instance GHC.Base.Functor Control.Eff.Concurrent.Process.MessageSelector instance GHC.Base.Semigroup a => GHC.Base.Monoid (Control.Eff.Concurrent.Process.MessageSelector a) instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Control.Eff.Concurrent.Process.MessageSelector a) instance GHC.Show.Show (Control.Eff.Concurrent.Process.Process r b) instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Control.Eff.Concurrent.Process.ResumeProcess a) instance Control.DeepSeq.NFData1 Control.Eff.Concurrent.Process.ResumeProcess instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.ProcessDown instance GHC.Show.Show Control.Eff.Concurrent.Process.ProcessDown instance GHC.Classes.Ord Control.Eff.Concurrent.Process.SomeExitReason instance GHC.Classes.Eq Control.Eff.Concurrent.Process.SomeExitReason instance GHC.Show.Show Control.Eff.Concurrent.Process.SomeExitReason instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.SomeExitReason instance GHC.Show.Show (Control.Eff.Concurrent.Process.ExitReason x) instance GHC.Exception.Type.Exception (Control.Eff.Concurrent.Process.ExitReason 'Control.Eff.Concurrent.Process.Recoverable) instance GHC.Exception.Type.Exception (Control.Eff.Concurrent.Process.ExitReason 'Control.Eff.Concurrent.Process.NoRecovery) instance Control.DeepSeq.NFData (Control.Eff.Concurrent.Process.ExitReason x) instance GHC.Classes.Ord (Control.Eff.Concurrent.Process.ExitReason x) instance GHC.Classes.Eq (Control.Eff.Concurrent.Process.ExitReason 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 -- | Functions for timeouts when receiving messages. -- -- NOTE: If you use a single threaded scheduler, these functions will not -- work as expected. (This is an open TODO) module Control.Eff.Concurrent.Process.Timer -- | A number of micro seconds. data Timeout -- | The reference to a timer started by startTimer, required to -- stop a timer via cancelTimer. data TimerReference -- | A value to be sent when timer started with startTimer has -- elapsed. data TimerElapsed -- | Send a message to a given process after waiting. The message is -- created by applying the function parameter to the -- TimerReference, such that the message can directly refer to the -- timer. sendAfter :: forall r q message. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable message, NFData message) => ProcessId -> Timeout -> (TimerReference -> message) -> Eff r TimerReference -- | Start a new timer, after the time has elapsed, TimerElapsed is -- sent to calling process. The message also contains the -- TimerReference returned by this function. Use -- cancelTimer to cancel the timer. Use selectTimerElapsed -- to receive the message using receiveSelectedMessage. To receive -- messages with guarded with a timeout see receiveAfter. startTimer :: forall r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Timeout -> Eff r TimerReference -- | Cancel a timer started with startTimer. cancelTimer :: forall r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => TimerReference -> Eff r () -- | A MessageSelector matching TimerElapsed messages created -- by startTimer. selectTimerElapsed :: TimerReference -> MessageSelector TimerElapsed -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfter. receiveAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable a, NFData a, Show a) => Timeout -> Eff r (Maybe a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Left TimerElapsed. This -- is based on selectTimerElapsed and startTimer. receiveSelectedAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Show a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) instance GHC.Classes.Eq Control.Eff.Concurrent.Process.Timer.TimerElapsed instance GHC.Classes.Ord Control.Eff.Concurrent.Process.Timer.TimerElapsed instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.Timer.TimerElapsed instance GHC.Enum.Enum Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Real.Real Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Real.Integral Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Num.Num Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Classes.Eq Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Classes.Ord Control.Eff.Concurrent.Process.Timer.TimerReference instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Enum.Enum Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Real.Real Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Real.Integral Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Num.Num Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Classes.Eq Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Classes.Ord Control.Eff.Concurrent.Process.Timer.Timeout instance Control.DeepSeq.NFData Control.Eff.Concurrent.Process.Timer.Timeout instance GHC.Show.Show Control.Eff.Concurrent.Process.Timer.TimerElapsed instance GHC.Show.Show Control.Eff.Concurrent.Process.Timer.TimerReference instance GHC.Show.Show Control.Eff.Concurrent.Process.Timer.Timeout -- | This module contains a mechanism to specify what kind of messages (aka -- requests) a Server (Process) can handle, and if -- the caller blocks and waits for an answer, which the server process -- provides. -- -- The type magic in the Api type familiy allows to define a -- related set of requests along with the corresponding responses. -- -- Request handling can be either blocking, if a response is requred, or -- non-blocking. -- -- A process can serve a specific Api instance by using the -- functions provided by the Control.Eff.Concurrent.Api.Server -- module. -- -- To enable a process to use such a service, the functions -- provided by the Control.Eff.Concurrent.Api.Client should be -- used. module Control.Eff.Concurrent.Api -- | This data family defines an API, a communication interface description -- between at least two processes. The processes act as servers or -- client(s) regarding a specific instance of this type. -- -- The first parameter is usually a user defined phantom type that -- identifies the Api instance. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Api instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   data instance Api BookShop r where
--     RentBook  :: BookId   -> Api BookShop ('Synchronous (Either RentalError RentalId))
--     BringBack :: RentalId -> Api BookShop 'Asynchronous
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
data family Api (api :: Type) (reply :: Synchronicity) -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Api -- instance. data Synchronicity -- | Specify that handling a request is a blocking operation with a -- specific return type, e.g. ('Synchronous (Either RentalError -- RentalId)) Synchronous :: Type -> Synchronicity -- | Non-blocking, asynchronous, request handling Asynchronous :: Synchronicity -- | This is a tag-type that wraps around a ProcessId and holds an -- Api index type. newtype Server api Server :: ProcessId -> Server api [_fromServer] :: Server api -> ProcessId fromServer :: forall api_aSTl api_aT2L. Iso (Server api_aSTl) (Server api_aT2L) ProcessId ProcessId -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API proxyAsServer :: proxy api -> ProcessId -> Server api -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API asServer :: forall api. ProcessId -> Server api instance forall k (api :: k). GHC.Classes.Ord (Control.Eff.Concurrent.Api.Server api) instance forall k (api :: k). GHC.Classes.Eq (Control.Eff.Concurrent.Api.Server api) instance forall k (api :: k). Data.Typeable.Internal.Typeable api => GHC.Show.Show (Control.Eff.Concurrent.Api.Server api) -- | Functions for Api clients. -- -- This modules is required to write clients that consume an Api. module Control.Eff.Concurrent.Api.Client -- | Send an Api request that has no return value and return as fast -- as possible. The type signature enforces that the corresponding -- Api clause is Asynchronous. The operation never fails, -- if it is important to know if the message was delivered, use -- call instead. cast :: forall r q o. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable o, Typeable (Api o 'Asynchronous)) => Server o -> Api o 'Asynchronous -> Eff r () -- | Send an Api request and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Api clause -- is Synchronous. call :: forall result api r q. (SetMember Process (Process q) r, Member Interrupts r, Typeable api, Typeable (Api api ( 'Synchronous result)), Typeable result, HasCallStack, NFData result, Show result) => Server api -> Api api ( 'Synchronous result) -> Eff r result -- | Like cast but take the Server from the reader provided -- by registerServer. castRegistered :: (Typeable o, ServesApi o r q, HasCallStack, Member Interrupts r) => Api o 'Asynchronous -> Eff r () -- | Like call but take the Server from the reader provided -- by registerServer. callRegistered :: (Typeable reply, ServesApi o r q, HasCallStack, NFData reply, Show reply, Member Interrupts r) => Api o ( 'Synchronous reply) -> Eff r reply -- | Instead of passing around a Server value and passing to -- functions like cast or call, a Server can -- provided by a Reader effect, if there is only a single -- server for a given Api instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Server. type ServesApi o r q = (Typeable o, SetMember Process (Process q) r, Member (ServerReader o) r) -- | The reader effect for ProcessIds for Apis, see -- registerServer type ServerReader o = Reader (Server o) -- | Get the Server registered with registerServer. whereIsServer :: Member (ServerReader o) e => Eff e (Server o) -- | Run a reader effect that contains the one server handling a -- specific Api instance. registerServer :: HasCallStack => Server o -> Eff (ServerReader o : r) a -> Eff r a -- | This module provides support for executing Process actions from -- IO. -- -- One use case is interacting with processes from the REPL, e.g.: -- --
--   >>> import Control.Eff.Concurrent.Process.SingleThreadedScheduler (defaultMain)
--   
-- --
--   >>> import Control.Eff.Loop
--   
-- --
--   >>> import Data.Dynamic
--   
-- --
--   >>> import Data.Maybe
--   
-- --
--   >>> s <- forkInteractiveScheduler Control.Eff.Concurrent.Process.SingleThreadedScheduler.defaultMain
--   
-- --
--   >>> fooPid <- submit s (spawn (foreverCheap (receiveAnyMessage SP >>= (logMsg . fromMaybe "Huh!??" . fromDynamic))))
--   
-- --
--   >>> fooPid
--   <0.1.0>
--   
-- --
--   >>> submit s (sendMessageAs SP fooPid "test")
--   test
--   
-- --
--   >>> submit s (sendShutdown SP fooPid)
--   
module Control.Eff.Concurrent.Process.Interactive -- | Contains the communication channels to interact with a scheduler -- running in its' own thread. data SchedulerSession r -- | Fork a scheduler with a process that communicates with it via -- MVar, which is also the reason for the Lift IO -- constraint. forkInteractiveScheduler :: forall r. SetMember Lift (Lift IO) r => (Eff (InterruptableProcess r) () -> IO ()) -> IO (SchedulerSession r) -- | Exit the schedulder immediately using an asynchronous exception. killInteractiveScheduler :: SchedulerSession r -> IO () -- | Send a Process effect to the main process of a scheduler, this -- blocks until the effect is executed. submit :: forall r a. SetMember Lift (Lift IO) r => SchedulerSession r -> Eff (InterruptableProcess r) a -> IO a -- | Combination of submit and cast. submitCast :: forall o r. (SetMember Lift (Lift IO) r, Typeable o, Member Interrupts r) => SchedulerSession r -> Server o -> Api o 'Asynchronous -> IO () -- | Combination of submit and cast. submitCall :: forall o q r. (SetMember Lift (Lift IO) r, Typeable o, Typeable q, NFData q, Show q, Member Interrupts r) => SchedulerSession r -> Server o -> Api o ( 'Synchronous q) -> IO q -- | Asynchronous Logging module Control.Eff.Log.Channel -- | Fork a new process in which the given log message writer, will listen -- on a message queue, to which all log message will be relayed. -- -- 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: -- --
--   exampleAsyncLogging :: IO ()
--   exampleAsyncLogging =
--       runLift
--     $ withSomeLogging @IO
--     $ withAsyncLogging (1000::Int) consoleLogWriter
--     $ do logMsg "test 1"
--          logMsg "test 2"
--          logMsg "test 3"
--   
withAsyncLogging :: (LogsTo IO e, Lifted IO e, MonadBaseControl IO (Eff e), Integral len) => len -> LogWriter IO -> Eff e a -> Eff e a -- | A logging effect. -- -- There is just one log message type: LogMessage and it is -- written using logMsg and the functions built on top of it. -- -- The Logs effect is tightly coupled with the -- LogWriterReader effect. When using the -- ControlMonadBaseControl instance, the underlying monad of the -- LogWriter, that is expected to be present through the -- respective LogWriterReader, is constrained to be the base monad -- itself, e.g. IO. -- -- The log message type is fixed to LogMessage, and there is a -- type class for converting to that, call ToLogMessage. -- -- There is a single global LogPredicate that can be used to -- suppress logs directly at the point where they are sent, in the -- logMsg function. -- -- Note that all logging is eventually done via logMsg; -- logMsg is the only place where log filtering should -- happen. -- -- Also, LogMessages are evaluated using deepseq, -- after they pass the LogPredicate, also inside -- logMsg. -- -- 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"
--   
-- --

Asynchronous Logging

-- -- Logging in a withAsync spawned thread is done using -- withAsyncLogging. -- --

LogPredicates

-- -- See Control.Eff.Log.Handler#LogPredicate module Control.Eff.Log -- | Example code for: -- -- exampleLogging :: IO () -- | Example code for: -- -- exampleWithLogging :: IO () -- | Example code for: -- -- exampleWithSomeLogging :: () -- | Example code for: -- -- exampleLogPredicate :: IO Int -- | Example code for: -- -- exampleLogCapture :: IO () -- | Example code for: -- -- exampleAsyncLogging :: IO () -- | A coroutine based, single threaded scheduler for Processes. module Control.Eff.Concurrent.Process.SingleThreadedScheduler -- | Handle the Process effect, as well as all lower effects using -- an effect handler function. -- -- Execute the main Process and all the other processes -- spawned by it in the current thread concurrently, using a -- co-routine based, round-robin scheduler. If a process exits with eg.g -- exitNormally or exitWithError or is killed by another -- process Left ... is returned. Otherwise, the result will be -- wrapped in a Right. -- -- Every time a process _yields_ the effects are evaluated down to the a -- value of type m (Either String a). -- -- If the evaluator function runs the action down e.g. IO this -- might improve memory consumption, for long running services, with -- processes that loop endlessly. scheduleM :: forall m r a. Monad m => (forall b. Eff r b -> m b) -> m () -> Eff (InterruptableProcess r) a -> m (Either (ExitReason 'NoRecovery) a) -- | Like scheduleIO but pure. The yield effect is -- just return (). schedulePure == runIdentity . -- scheduleM (Identity . run) (return ()) schedulePure :: Eff (InterruptableProcess '[Logs, LogWriterReader PureLogWriter]) a -> Either (ExitReason 'NoRecovery) a -- | Invoke scheduleM with lift yield as yield -- effect. scheduleIO runEff == scheduleM (runLift . runEff) -- (liftIO yield) scheduleIO :: MonadIO m => (forall b. Eff r b -> Eff '[Lift m] b) -> Eff (InterruptableProcess r) a -> m (Either (ExitReason 'NoRecovery) a) -- | Invoke scheduleM with lift yield as yield -- effect. scheduleMonadIOEff == scheduleM id (liftIO -- yield) scheduleMonadIOEff :: MonadIO (Eff r) => Eff (InterruptableProcess r) a -> Eff r (Either (ExitReason 'NoRecovery) a) -- | Run processes that have the Logs and the Lift effects. -- The user must provide a log handler function. -- -- Log messages are evaluated strict. -- --
--   scheduleIOWithLogging == scheduleIO . withLogging
--   
scheduleIOWithLogging :: HasCallStack => LogWriter IO -> Eff (InterruptableProcess LoggingAndIo) a -> IO (Either (ExitReason 'NoRecovery) a) -- | Execute a Process using scheduleM on top of Lift -- IO and withLogging String effects. defaultMainSingleThreaded :: HasCallStack => Eff (InterruptableProcess LoggingAndIo) () -> IO () instance GHC.Show.Show (Control.Eff.Concurrent.Process.SingleThreadedScheduler.OnYield r a) instance GHC.Show.Show (Control.Eff.Concurrent.Process.SingleThreadedScheduler.STS r m) -- | Implement Erlang style message passing concurrency. -- -- This module contains spawn which handles the Process -- effects, using TQueues and withAsync. -- -- This aims to be a pragmatic implementation, so even logging is -- supported. -- -- At the core is a main process that enters schedule and -- creates all of the internal state stored in TVars to manage -- processes with message queues. module Control.Eff.Concurrent.Process.ForkIOScheduler -- | This is the main entry point to running a message passing concurrency -- application. This function takes a Process on top of the -- SchedulerIO effect for concurrent logging. schedule :: HasCallStack => Eff InterruptableProcEff () -> Eff LoggingAndIo () -- | Start the message passing concurrency system then execute a -- Process on top of SchedulerIO effect. All logging is -- sent to standard output. defaultMain :: HasCallStack => Eff InterruptableProcEff () -> IO () -- | Start the message passing concurrency system then execute a -- Process on top of SchedulerIO effect. All logging is -- sent to standard output. defaultMainWithLogWriter :: HasCallStack => LogWriter IO -> Eff InterruptableProcEff () -> IO () -- | The concrete list of Effects of processes compatible with this -- scheduler. This builds upon SchedulerIO. type ProcEff = ConsProcess SchedulerIO -- | The concrete list of the effects, that the Process uses type InterruptableProcEff = InterruptableProcess SchedulerIO -- | The concrete list of Effects for this scheduler implementation. type SchedulerIO = (Reader SchedulerState : LoggingAndIo) -- | Type class constraint to indicate that an effect union contains the -- effects required by every process and the scheduler implementation -- itself. type HasSchedulerIO r = (HasCallStack, Lifted IO r, SchedulerIO <:: r) instance GHC.Show.Show Control.Eff.Concurrent.Process.ForkIOScheduler.ProcessInfo instance Data.Default.Class.Default Control.Eff.Concurrent.Process.ForkIOScheduler.MessageQ -- | Support code to implement Api _server_ processes. module Control.Eff.Concurrent.Api.Server -- | Serve an Api in a newly spawned process. spawnApiServer :: forall api eff. (ToServerPids api, HasCallStack) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Serve an Api in a newly spawned -and linked - process. spawnLinkApiServer :: forall api eff. (ToServerPids api, HasCallStack) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; the callbacks -- have access to some state initialed by the function in the first -- parameter. spawnApiServerStateful :: forall api eff state. (HasCallStack, ToServerPids api) => Eff (InterruptableProcess eff) state -> MessageCallback api (State state : InterruptableProcess eff) -> InterruptCallback (State state : ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. spawnApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. Links to the calling process like linkProcess would. spawnLinkApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | Receive loop for Api calls. This starts a receive loop -- for a MessageCallback. It is used behind the scenes by -- spawnLinkApiServerEffectful and spawnApiServerEffectful. apiServerLoop :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff) => MessageCallback api serverEff -> InterruptCallback serverEff -> Eff serverEff () -- | A command to the server loop started by apiServerLoop. -- Typically returned by a MessageCallback to indicate if the -- server should continue or stop. data CallbackResult -- | Tell the server to keep the server loop running [AwaitNext] :: CallbackResult -- | Tell the server to exit, this will cause apiServerLoop to stop -- handling requests without exiting the process. [StopServer] :: InterruptReason -> CallbackResult -- | An existential wrapper around a MessageSelector and a function -- that handles the selected message. The api type parameter is -- a phantom type. -- -- The return value if the handler function is a CallbackResult. data MessageCallback api eff [MessageCallback] :: MessageSelector a -> (a -> Eff eff CallbackResult) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleCasts :: forall api eff. (HasCallStack, Typeable api, Typeable (Api api 'Asynchronous)) => (Api api 'Asynchronous -> Eff eff CallbackResult) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks -- --

Example

-- --
--   handleCalls
--     ( (RentBook bookId customerId) runCall ->
--        runCall $ do
--            rentalIdE <- rentBook bookId customerId
--            case rentalIdE of
--              -- on fail we just don't send a reply, let the caller run into
--              -- timeout
--              Left err -> return (Nothing, AwaitNext)
--              Right rentalId -> return (Just rentalId, AwaitNext))
--   
handleCalls :: forall api eff effScheduler. (HasCallStack, Typeable api, SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (forall secret reply. (Typeable reply, Typeable (Api api ( 'Synchronous reply))) => Api api ( 'Synchronous reply) -> (Eff eff (Maybe reply, CallbackResult) -> secret) -> secret) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleCastsAndCalls :: forall api eff effScheduler. (HasCallStack, Typeable api, Typeable (Api api 'Asynchronous), SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (Api api 'Asynchronous -> Eff eff CallbackResult) -> (forall secret reply. (Typeable reply, Typeable (Api api ( 'Synchronous reply))) => Api api ( 'Synchronous reply) -> (Eff eff (Maybe reply, CallbackResult) -> secret) -> secret) -> MessageCallback api eff -- | A variation of handleCalls that allows to defer a reply to a -- call. handleCallsDeferred :: forall api eff effScheduler. (HasCallStack, Typeable api, SetMember Process (Process effScheduler) eff, Member Interrupts eff) => (forall reply. (Typeable reply, Typeable (Api api ( 'Synchronous reply))) => RequestOrigin (Api api ( 'Synchronous reply)) -> Api api ( 'Synchronous reply) -> Eff eff CallbackResult) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks handleMessages :: forall eff a. (HasCallStack, NFData a, Typeable a) => (a -> Eff eff CallbackResult) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleSelectedMessages :: forall eff a. HasCallStack => MessageSelector a -> (a -> Eff eff CallbackResult) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleAnyMessages :: forall eff. HasCallStack => (Dynamic -> Eff eff CallbackResult) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleProcessDowns :: forall eff. HasCallStack => (MonitorReference -> Eff eff CallbackResult) -> MessageCallback '[] eff -- | A fallbackHandler that drops the left-over messages. dropUnhandledMessages :: forall eff. HasCallStack => MessageCallback '[] eff -- | A fallbackHandler that terminates if there are unhandled -- messages. exitOnUnhandled :: forall eff. HasCallStack => MessageCallback '[] eff -- | A fallbackHandler that drops the left-over messages. logUnhandledMessages :: forall eff. (Member Logs eff, HasCallStack) => MessageCallback '[] eff -- | Compose two Apis to a type-level pair of them. -- --
--   handleCalls api1calls ^: handleCalls api2calls ^:
--   
(^:) :: forall (api1 :: Type) (apis2 :: [Type]) eff. HasCallStack => MessageCallback api1 eff -> MessageCallback apis2 eff -> MessageCallback (api1 : apis2) eff infixr 5 ^: -- | Make a fallback handler, i.e. a handler to which no other can be -- composed to from the right. fallbackHandler :: forall api eff. HasCallStack => MessageCallback api eff -> MessageCallback '[] eff -- | Helper type class for the return values of spawnApiServer et -- al. class ToServerPids (t :: k) where { type family ServerPids t; } toServerPids :: ToServerPids t => proxy t -> ProcessId -> ServerPids t -- | Just a wrapper around a function that will be applied to the result of -- a MessageCallbacks StopServer clause, or an -- InterruptReason caught during the execution of receive -- or a MessageCallback data InterruptCallback eff [InterruptCallback] :: (InterruptReason -> Eff eff CallbackResult) -> InterruptCallback eff -- | A smart constructor for InterruptCallbacks stopServerOnInterrupt :: forall eff. HasCallStack => InterruptCallback eff instance Data.Default.Class.Default (Control.Eff.Concurrent.Api.Server.InterruptCallback eff) instance Control.Eff.Concurrent.Api.Server.ToServerPids '[] instance (Control.Eff.Concurrent.Api.Server.ToServerPids api1, Control.Eff.Concurrent.Api.Server.ToServerPids api2) => Control.Eff.Concurrent.Api.Server.ToServerPids (api1 : api2) instance Control.Eff.Concurrent.Api.Server.ToServerPids api1 => Control.Eff.Concurrent.Api.Server.ToServerPids api1 instance forall k (api :: k) (eff :: [* -> *]). GHC.Base.Semigroup (Control.Eff.Concurrent.Api.Server.MessageCallback api eff) instance forall k (api :: k) (eff :: [* -> *]). GHC.Base.Monoid (Control.Eff.Concurrent.Api.Server.MessageCallback api eff) instance forall k (api :: k) (eff :: [* -> *]). Data.Default.Class.Default (Control.Eff.Concurrent.Api.Server.MessageCallback api eff) -- | Observer Effects -- -- This module supports the implementation of observers and observables. -- Expected use case is event propagation. module Control.Eff.Concurrent.Api.Observer -- | Describes a process that observes another via Asynchronous -- Api messages. -- -- An observer consists of a filter and a process id. The filter converts -- an observation to a message understood by the observer process, and -- the ProcessId is used to send the message. data Observer o [Observer] :: (Show (Server p), Typeable p, Typeable o) => (o -> Maybe (Api p 'Asynchronous)) -> Server p -> Observer o -- | This data family defines an API, a communication interface description -- between at least two processes. The processes act as servers or -- client(s) regarding a specific instance of this type. -- -- The first parameter is usually a user defined phantom type that -- identifies the Api instance. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Api instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   data instance Api BookShop r where
--     RentBook  :: BookId   -> Api BookShop ('Synchronous (Either RentalError RentalId))
--     BringBack :: RentalId -> Api BookShop 'Asynchronous
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
data family Api (api :: Type) (reply :: Synchronicity) -- | And an Observer to the set of reciepients for all observations -- reported by observed. Note that the observers are keyed by the -- observing process, i.e. a previous entry for the process contained in -- the Observer is overwritten. If you want multiple entries for a -- single process, just combine several filter functions. registerObserver :: (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o) => Observer o -> Server (ObserverRegistry o) -> Eff r () -- | Send the ForgetObserver message forgetObserver :: (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o) => Observer o -> Server (ObserverRegistry o) -> Eff r () -- | Based on the Api instance for Observer this simplified -- writing a callback handler for observations. In order to register to -- and ObserverRegistry use toObserver. handleObservations :: (HasCallStack, Typeable o, SetMember Process (Process q) r) => (o -> Eff r CallbackResult) -> MessageCallback (Observer o) r -- | Use a Server as an Observer for -- handleObservations. toObserver :: Typeable o => Server (Observer o) -> Observer o -- | Create an Observer that conditionally accepts all observations -- of the given type and applies the given function to them; the function -- takes an observation and returns an Api cast that the observer -- server is compatible to. toObserverFor :: (Typeable a, Typeable o) => (o -> Api a 'Asynchronous) -> Server a -> Observer o -- | An Api for managing Observers, encompassing registration -- and de-registration of Observers. data ObserverRegistry o -- | Alias for the effect that contains the observers managed by -- manageObservers type ObserverState o = State (Observers o) -- | Provide the implementation for the ObserverRegistry Api, this -- handled RegisterObserver and ForgetObserver messages. It -- also adds the ObserverState constraint to the effect list. handleObserverRegistration :: forall o q r. (HasCallStack, Typeable o, SetMember Process (Process q) r, Member (ObserverState o) r) => MessageCallback (ObserverRegistry o) r -- | Keep track of registered Observers. -- -- Handle the ObserverState introduced by -- handleObserverRegistration. manageObservers :: Eff (ObserverState o : r) a -> Eff r a -- | Report an observation to all observers. The process needs to -- manageObservers and to handleObserverRegistration. observed :: forall o r q. (SetMember Process (Process q) r, Member (ObserverState o) r, Member Interrupts r) => o -> Eff r () instance GHC.Show.Show (Control.Eff.Concurrent.Api.Observer.Observer o) instance GHC.Classes.Ord (Control.Eff.Concurrent.Api.Observer.Observer o) instance GHC.Classes.Eq (Control.Eff.Concurrent.Api.Observer.Observer o) -- | A small process to capture and _share_ observation's by enqueueing -- them into an STM TBQueue. module Control.Eff.Concurrent.Api.Observer.Queue -- | Contains a TBQueue capturing observations. See -- spawnLinkObservationQueueWriter, readObservationQueue. data ObservationQueue a -- | A Reader for an ObservationQueue. type ObservationQueueReader a = Reader (ObservationQueue a) -- | Read queued observations captured and enqueued in the shared -- TBQueue by spawnLinkObservationQueueWriter. This blocks -- until something was captured or an interrupt or exceptions was thrown. -- For a non-blocking variant use tryReadObservationQueue or -- flushObservationQueue. readObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r o -- | Read queued observations captured and enqueued in the shared -- TBQueue by spawnLinkObservationQueueWriter. Return the -- oldest enqueued observation immediately or Nothing if the queue -- is empty. Use readObservationQueue to block until an -- observation is observed. tryReadObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r (Maybe o) -- | Read at once all currently queued observations captured and enqueued -- in the shared TBQueue by -- spawnLinkObservationQueueWriter. This returns immediately all -- currently enqueued observations. For a blocking variant use -- readObservationQueue. flushObservationQueue :: forall o r. (Member (ObservationQueueReader o) r, HasCallStack, MonadIO (Eff r), Typeable o, Member Logs r) => Eff r [o] -- | Create a mutable queue for observations. Use -- spawnLinkObservationQueueWriter for a simple way to get a -- process that enqueues all observations. -- --

Example

-- --
--   withObservationQueue 100 $ do
--     q  <- ask @(ObservationQueueReader TestEvent)
--     wq <- spawnLinkObservationQueueWriter q
--     registerObserver wq testServer
--     ...
--     cast testServer DoSomething
--     evt <- readObservationQueue @TestEvent
--     ...
--   
withObservationQueue :: forall o b e len. (HasCallStack, Typeable o, Show o, Member Logs e, Lifted IO e, Integral len, Member Interrupts e) => len -> Eff (ObservationQueueReader o : e) b -> Eff e b -- | Spawn a process that can be used as an Observer that enqueues -- the observations into an ObservationQueue. See -- withObservationQueue for an example. -- -- The observations can be obtained by readObservationQueue. All -- observations are captured up to the queue size limit, such that the -- first message received will be first message returned by -- readObservationQueue. spawnLinkObservationQueueWriter :: forall o q. (Typeable o, Show o, Member Logs q, Lifted IO q, HasCallStack) => ObservationQueue o -> Eff (InterruptableProcess q) (Observer o) -- | 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 comperative heap profiling of these function vs. their -- counterparts in the base package. -- -- Here are the images of the profiling results, the images show that the -- functions in this module do not leak space, compared to the original -- functions (forever and replicateM_): -- module Control.Eff.Loop -- | A version of forever that hopefully tricks GHC into -- not creating a space leak. The intuition is, that we -- want to do something that is cheap, and hence should be -- recomputed instead of shared. foreverCheap :: Monad m => m a -> m () -- | A version of replicateM_ that hopefully tricks GHC into -- not creating a space leak. The intuition is, that we -- want to do something that is cheap, and hence should be -- recomputed instead of shared. replicateCheapM_ :: Monad m => Int -> m a -> m () -- | Erlang style processes with message passing concurrency based on -- (more) extensible-effects. module Control.Eff.Concurrent -- | Each process is identified by a single process id, that stays constant -- throughout the life cycle of a process. Also, message sending relies -- on these values to address messages to processes. newtype ProcessId ProcessId :: Int -> ProcessId [_fromProcessId] :: ProcessId -> Int -- | A monitored process exited. This message is sent to a process by the -- scheduler, when a process that was monitored died. data ProcessDown ProcessDown :: !MonitorReference -> !SomeExitReason -> ProcessDown [downReference] :: ProcessDown -> !MonitorReference [downReason] :: ProcessDown -> !SomeExitReason -- | A value that contains a unique reference of a process monitoring. data MonitorReference MonitorReference :: Int -> ProcessId -> MonitorReference [monitorIndex] :: MonitorReference -> Int [monitoredProcess] :: MonitorReference -> ProcessId -- | An existential wrapper around ExitReason data SomeExitReason [SomeExitReason] :: ExitReason x -> SomeExitReason -- | This adds a layer of the Interrupts effect on top of -- ConsProcess type InterruptableProcess e = Interrupts : ConsProcess e -- | Exceptions containing InterruptReasons. See -- handleInterrupts, exitOnInterrupt or -- provideInterrupts type Interrupts = Exc InterruptReason -- | ExitReasons which are recoverable are interrupts. type InterruptReason = ExitReason 'Recoverable -- | A sum-type with reasons for why a process exists the scheduling loop, -- this includes errors, that can occur when scheduling messages. data ExitReason (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) [ProcessFinished] :: ExitReason 'Recoverable -- | A process that should be running was not running. [ProcessNotRunning] :: ProcessId -> ExitReason 'Recoverable -- | A linked process is down [LinkedProcessCrashed] :: ProcessId -> ExitReason 'Recoverable -- | An exit reason that has an error message but isn't Recoverable. [ProcessError] :: String -> ExitReason 'Recoverable -- | A process function returned or exited without any error. [ExitNormally] :: ExitReason 'NoRecovery -- | An unhandled Recoverable allows NoRecovery. [NotRecovered] :: ExitReason 'Recoverable -> ExitReason 'NoRecovery -- | An unexpected runtime exception was thrown, i.e. an exception derived -- from SomeException [UnexpectedException] :: String -> String -> ExitReason 'NoRecovery -- | A process was cancelled (e.g. killed, in cancel) [Killed] :: ExitReason '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 ExitReason can be treated -- like a short interrupt which can be handled or ignored. data ExitRecovery Recoverable :: ExitRecovery NoRecovery :: ExitRecovery -- | The state that a Process is currently in. data ProcessState -- | The process has just been started but not scheduled yet. ProcessBooting :: ProcessState -- | The process yielded it's time slice ProcessIdle :: ProcessState -- | The process is busy with non-blocking ProcessBusy :: ProcessState -- | The process is busy with sending a message ProcessBusySending :: ProcessState -- | The process is busy with killing ProcessBusySendingShutdown :: ProcessState -- | The process is busy with killing ProcessBusySendingInterrupt :: ProcessState -- | The process blocked by a receiveAnyMessage ProcessBusyReceiving :: ProcessState -- | The process blocked by a linkProcess ProcessBusyLinking :: ProcessState -- | The process blocked by a unlinkProcess ProcessBusyUnlinking :: ProcessState -- | The process blocked by a monitor ProcessBusyMonitoring :: ProcessState -- | The process blocked by a demonitor ProcessBusyDemonitoring :: ProcessState -- | The process was interrupted ProcessInterrupted :: ProcessState -- | The process was shutdown or crashed ProcessShuttingDown :: ProcessState -- | Cons Process onto a list of effects. type ConsProcess r = Process r : r -- | Every function for Process things needs such a proxy value for -- the low-level effect list, i.e. the effects identified by -- r in Process r : r, this might be -- dependent on the scheduler implementation. data SchedulerProxy :: [Type -> Type] -> Type -- | Tell the type checker what effects we have below Process [SchedulerProxy] :: SchedulerProxy q -- | Like SchedulerProxy but shorter [SP] :: SchedulerProxy q -- | Like SP but different [Scheduler] :: SchedulerProxy q -- | 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 -- ExitReason. If isRecoverable holds for the given reason, -- the process may choose to continue. [Interrupted] :: InterruptReason -> ResumeProcess v -- | The process may resume to do work, using the given result. [ResumeWith] :: a -> ResumeProcess a -- | 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 [Dynamic]) -- | In cooperative schedulers, this will give processing time to the -- scheduler. Every other operation implicitly serves the same purpose. [YieldProcess] :: Process r (ResumeProcess ()) -- | Return the current ProcessId [SelfPid] :: Process r (ResumeProcess ProcessId) -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. [Spawn] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Start a new process, and Link to it . [SpawnLink] :: Eff (Process r : r) () -> Process r (ResumeProcess ProcessId) -- | Get the process state (or Nothing if the process is dead) [GetProcessState] :: ProcessId -> Process r (ResumeProcess (Maybe ProcessState)) -- | Shutdown the process; irregardless of the exit reason, this function -- never returns, [Shutdown] :: ExitReason 'NoRecovery -> Process r a -- | Raise an error, that can be handled. [SendShutdown] :: ProcessId -> ExitReason '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 -> InterruptReason -> 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 -> Dynamic -> 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 ExitReason -- LinkedProcessCrashed. [Link] :: ProcessId -> Process r (ResumeProcess ()) -- | Unlink the calling process from the other process. [Unlink] :: ProcessId -> Process r (ResumeProcess ()) -- | Create a message selector for a value that can be obtained by -- fromDynamic. It will also force the result. selectMessage :: (NFData t, Typeable t) => MessageSelector t -- | Create a message selector for a value that can be obtained by -- fromDynamic. It will also force the result. selectMessageLazy :: Typeable t => MessageSelector t -- | Create a message selector from a predicate. It will force the -- result. filterMessage :: (Typeable a, NFData a) => (a -> Bool) -> MessageSelector a -- | Create a message selector from a predicate. It will force the -- result. filterMessageLazy :: 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). It will force the result. selectMessageWith :: (Typeable a, NFData b) => (a -> Maybe b) -> MessageSelector b -- | 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). It will force the result. selectMessageWithLazy :: Typeable a => (a -> Maybe b) -> MessageSelector b -- | Create a message selector. It will force the result. selectDynamicMessage :: NFData a => (Dynamic -> Maybe a) -> MessageSelector a -- | Create a message selector. selectDynamicMessageLazy :: (Dynamic -> Maybe a) -> MessageSelector a -- | Create a message selector that will match every message. This is -- lazy because the result is not forceed. selectAnyMessageLazy :: MessageSelector Dynamic -- | Create a message selector for a value that can be obtained by -- fromDynamic with a proxy argument. It will also force -- the result. selectMessageProxy :: forall proxy t. (NFData t, Typeable t) => proxy t -> MessageSelector t -- | Create a message selector for a value that can be obtained by -- fromDynamic with a proxy argument. It will also force -- the result. selectMessageProxyLazy :: forall proxy t. Typeable t => proxy t -> MessageSelector t -- | Return a SchedulerProxy for a Process effect. thisSchedulerProxy :: Eff (Process r : r) (SchedulerProxy r) -- | Get the ExitRecovery toExitRecovery :: ExitReason r -> ExitRecovery -- | Get the ExitSeverity of a ExitReason. toExitSeverity :: ExitReason e -> ExitSeverity -- | A predicate for linked process crashes. isBecauseDown :: Maybe ProcessId -> ExitReason r -> Bool -- | Handle all InterruptReasons of an InterruptableProcess -- by wrapping them up in NotRecovered and then do a process -- Shutdown. provideInterruptsShutdown :: forall e a. Eff (InterruptableProcess e) a -> Eff (ConsProcess e) a -- | Handle InterruptReasons 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) => (InterruptReason -> Eff r a) -> Eff r a -> Eff r a -- | Like handleInterrupts, but instead of passing the -- InterruptReason to a handler function, Either is -- returned. tryUninterrupted :: (HasCallStack, Member Interrupts r) => Eff r a -> Eff r (Either InterruptReason 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 InterruptReasons arising during process operations, e.g. -- when a linked process crashes while we wait in a -- receiveSelectedMessage via a call to interrupt. exitOnInterrupt :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r) => Eff r a -> Eff r a -- | Handle InterruptReasons 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 InterruptReason a) -- | Wrap all (left) InterruptReasons into NotRecovered and -- return the (right) NoRecovery ExitReasons as is. mergeEitherInterruptAndExitReason :: Either InterruptReason (ExitReason 'NoRecovery) -> ExitReason 'NoRecovery -- | Throw an InterruptReason, can be handled by -- handleInterrupts or exitOnInterrupt or -- provideInterrupts. interrupt :: (HasCallStack, Member Interrupts r) => InterruptReason -> Eff r a -- | A predicate for crashes. A crash happens when a process exits -- with an ExitReason other than ExitNormally isCrash :: ExitReason x -> Bool -- | A predicate for recoverable exit reasons. This predicate defines the -- exit reasons which functions such as executeAndResume isRecoverable :: ExitReason x -> Bool -- | Partition a SomeExitReason back into either a NoRecovery -- or a Recoverable ExitReason fromSomeExitReason :: SomeExitReason -> Either (ExitReason 'NoRecovery) InterruptReason -- | Print a ExitReason to Just a formatted String -- when isCrash is True. This can be useful in combination -- with view patterns, e.g.: -- --
--   logCrash :: ExitReason -> Eff e ()
--   logCrash (toCrashReason -> Just reason) = logError reason
--   logCrash _ = return ()
--   
-- -- Though this can be improved to: -- --
--   logCrash = traverse_ logError . toCrashReason
--   
toCrashReason :: ExitReason x -> Maybe String -- | Log the ExitReasons logProcessExit :: forall e x. (Member Logs e, HasCallStack) => ExitReason x -> Eff e () -- | Execute a and action and return the result; if the process is -- interrupted by an error or exception, or an explicit shutdown from -- another process, or through a crash of a linked process, i.e. whenever -- the exit reason satisfies isRecoverable, return the exit -- reason. executeAndResume :: forall q r v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r (Either (ExitReason 'Recoverable) v) -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrExit :: forall r q v. (SetMember Process (Process q) r, HasCallStack) => Process q (ResumeProcess v) -> Eff r v -- | Execute a Process action and resume the process, exit the -- process when an Interrupts was raised. Use -- executeAndResume to catch interrupts. executeAndResumeOrThrow :: forall q r v. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Process q (ResumeProcess v) -> Eff r v -- | Use executeAndResumeOrExit to execute YieldProcess. -- Refer to YieldProcess for more information. yieldProcess :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => Eff r () -- | Send a message to a process addressed by the ProcessId. See -- SendMessage. sendMessage :: forall r q o. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r, Typeable o) => ProcessId -> o -> Eff r () -- | Send a Dynamic value to a process addressed by the -- ProcessId. See SendMessage. sendAnyMessage :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> Dynamic -> Eff r () -- | Exit a process addressed by the ProcessId. The process will -- exit, it might do some cleanup, but is ultimately unrecoverable. See -- SendShutdown. sendShutdown :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> ExitReason 'NoRecovery -> Eff r () -- | Interrupts a process addressed by the ProcessId. The process -- might exit, or it may continue. | Like sendInterrupt, but also -- return True iff the process to exit exists. sendInterrupt :: forall r q. (SetMember Process (Process q) r, HasCallStack, Member Interrupts r) => ProcessId -> InterruptReason -> 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 -- InterruptReason wrapped in NotRecovered. For specific -- use cases it might be better to use spawnRaw. spawn :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Like spawn but return (). spawn_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r () -- | Start a new process, and immediately link to it. spawnLink :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (InterruptableProcess q) () -> Eff r ProcessId -- | Start a new process, the new process will execute an effect, the -- function will return immediately with a ProcessId. The spawned -- process has only the raw ConsProcess effects. For -- non-library code spawn might be better suited. spawnRaw :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r ProcessId -- | Like spawnRaw but return (). spawnRaw_ :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff (ConsProcess q) () -> Eff r () -- | Return True if the process is alive. isProcessAlive :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r Bool -- | Block until a message was received. See ReceiveSelectedMessage -- for more documentation. receiveAnyMessage :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r Dynamic -- | Block until a message was received, that is not Nothing after -- applying a callback to it. See ReceiveSelectedMessage for more -- documentation. receiveSelectedMessage :: forall r q a. (HasCallStack, Show a, SetMember Process (Process q) r, Member Interrupts r) => MessageSelector a -> Eff r a -- | Receive and cast the message to some Typeable instance. See -- ReceiveSelectedMessage for more documentation. This will wait -- for a message of the return type using receiveSelectedMessage receiveMessage :: forall a r q. (HasCallStack, Typeable a, Show a, SetMember Process (Process q) r, Member Interrupts r) => Eff r a -- | Remove and return all messages currently enqueued in the process -- message queue. flushMessages :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r [Dynamic] -- | 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 ExitReason, otherwise -- the process will be exited with the same reason using -- exitBecause. See also ReceiveSelectedMessage for more -- documentation. receiveSelectedLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => MessageSelector a -> (Either InterruptReason a -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Like receiveSelectedLoop but not selective. See also -- selectAnyMessageLazy, receiveSelectedLoop. receiveAnyLoop :: forall r q endOfLoopResult. (SetMember Process (Process q) r, HasCallStack) => (Either InterruptReason Dynamic -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Like receiveSelectedLoop but refined to casting to a specific -- Typeable using selectMessageLazy. receiveLoop :: forall r q a endOfLoopResult. (SetMember Process (Process q) r, HasCallStack, Typeable a) => (Either InterruptReason a -> Eff r (Maybe endOfLoopResult)) -> Eff r endOfLoopResult -- | Returns the ProcessId of the current process. self :: (HasCallStack, SetMember Process (Process q) r) => Eff r ProcessId -- | Generate a unique Int for the current process. makeReference :: (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Eff r Int -- | Monitor another process. When the monitored process exits a -- ProcessDown is sent to the calling process. The return value is -- a unique identifier for that monitor. There can be multiple monitors -- on the same process, and a message for each will be sent. If the -- process is already dead, the ProcessDown message will be sent -- immediately, without exit reason monitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r MonitorReference -- | Remove a monitor created with monitor. demonitor :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => MonitorReference -> Eff r () -- | monitor another process before while performing an action and -- demonitor afterwards. withMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> (MonitorReference -> Eff r a) -> Eff r a -- | A MessageSelector for receiving either a monitor of the given -- process or another message. receiveWithMonitor :: (HasCallStack, Member Interrupts r, SetMember Process (Process q) r, Member Interrupts r, Typeable a, Show a) => ProcessId -> MessageSelector a -> Eff r (Either ProcessDown a) -- | Make an InterruptReason for a ProcessDown message. -- -- For example: doSomething >>= either (interrupt . -- becauseProcessIsDown) return becauseProcessIsDown :: ProcessDown -> InterruptReason -- | A MessageSelector for the ProcessDown message of a -- specific process. selectProcessDown :: MonitorReference -> MessageSelector ProcessDown -- | Connect the calling process to another process, such that if one of -- the processes crashes (i.e. isCrash returns True), the -- other is shutdown with the ExitReason -- LinkedProcessCrashed. linkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Unlink the calling process from the other process. unlinkProcess :: forall r q. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => ProcessId -> Eff r () -- | Exit the process with a ExitReason. exitBecause :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => ExitReason 'NoRecovery -> Eff r a -- | Exit the process. exitNormally :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => Eff r a -- | Exit the process with an error. exitWithError :: forall r q a. (HasCallStack, SetMember Process (Process q) r) => String -> Eff r a fromProcessId :: Iso' ProcessId Int -- | A value to be sent when timer started with startTimer has -- elapsed. data TimerElapsed -- | The reference to a timer started by startTimer, required to -- stop a timer via cancelTimer. data TimerReference -- | A number of micro seconds. data Timeout -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Nothing. This is based on -- receiveSelectedAfter. receiveAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable a, NFData a, Show a) => Timeout -> Eff r (Maybe a) -- | Wait for a message of the given type for the given time. When no -- message arrives in time, return Left TimerElapsed. This -- is based on selectTimerElapsed and startTimer. receiveSelectedAfter :: forall a r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Show a) => MessageSelector a -> Timeout -> Eff r (Either TimerElapsed a) -- | A MessageSelector matching TimerElapsed messages created -- by startTimer. selectTimerElapsed :: TimerReference -> MessageSelector TimerElapsed -- | Send a message to a given process after waiting. The message is -- created by applying the function parameter to the -- TimerReference, such that the message can directly refer to the -- timer. sendAfter :: forall r q message. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable message, NFData message) => ProcessId -> Timeout -> (TimerReference -> message) -> Eff r TimerReference -- | Start a new timer, after the time has elapsed, TimerElapsed is -- sent to calling process. The message also contains the -- TimerReference returned by this function. Use -- cancelTimer to cancel the timer. Use selectTimerElapsed -- to receive the message using receiveSelectedMessage. To receive -- messages with guarded with a timeout see receiveAfter. startTimer :: forall r q. (Lifted IO q, HasCallStack, SetMember Process (Process q) r, Member Interrupts r) => Timeout -> Eff r TimerReference -- | This is a tag-type that wraps around a ProcessId and holds an -- Api index type. newtype Server api Server :: ProcessId -> Server api [_fromServer] :: Server api -> ProcessId -- | The (promoted) constructors of this type specify (at the type level) -- the reply behavior of a specific constructor of an Api -- instance. data Synchronicity -- | Specify that handling a request is a blocking operation with a -- specific return type, e.g. ('Synchronous (Either RentalError -- RentalId)) Synchronous :: Type -> Synchronicity -- | Non-blocking, asynchronous, request handling Asynchronous :: Synchronicity -- | This data family defines an API, a communication interface description -- between at least two processes. The processes act as servers or -- client(s) regarding a specific instance of this type. -- -- The first parameter is usually a user defined phantom type that -- identifies the Api instance. -- -- The second parameter specifies if a specific constructor of an -- (GADT-like) Api instance is Synchronous, i.e. returns -- a result and blocks the caller or if it is Asynchronous -- -- Example: -- --
--   data BookShop deriving Typeable
--   
--   data instance Api BookShop r where
--     RentBook  :: BookId   -> Api BookShop ('Synchronous (Either RentalError RentalId))
--     BringBack :: RentalId -> Api BookShop 'Asynchronous
--   
--   type BookId = Int
--   type RentalId = Int
--   type RentalError = String
--   
data family Api (api :: Type) (reply :: Synchronicity) fromServer :: forall api_aSTl api_aT2L. Iso (Server api_aSTl) (Server api_aT2L) ProcessId ProcessId -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API proxyAsServer :: proxy api -> ProcessId -> Server api -- | Tag a ProcessId with an Api type index to mark it a -- Server process handling that API asServer :: forall api. ProcessId -> Server api -- | The reader effect for ProcessIds for Apis, see -- registerServer type ServerReader o = Reader (Server o) -- | Instead of passing around a Server value and passing to -- functions like cast or call, a Server can -- provided by a Reader effect, if there is only a single -- server for a given Api instance. This type alias is -- convenience to express that an effect has Process and a reader -- for a Server. type ServesApi o r q = (Typeable o, SetMember Process (Process q) r, Member (ServerReader o) r) -- | Send an Api request that has no return value and return as fast -- as possible. The type signature enforces that the corresponding -- Api clause is Asynchronous. The operation never fails, -- if it is important to know if the message was delivered, use -- call instead. cast :: forall r q o. (HasCallStack, SetMember Process (Process q) r, Member Interrupts r, Typeable o, Typeable (Api o 'Asynchronous)) => Server o -> Api o 'Asynchronous -> Eff r () -- | Send an Api request and wait for the server to return a result -- value. -- -- The type signature enforces that the corresponding Api clause -- is Synchronous. call :: forall result api r q. (SetMember Process (Process q) r, Member Interrupts r, Typeable api, Typeable (Api api ( 'Synchronous result)), Typeable result, HasCallStack, NFData result, Show result) => Server api -> Api api ( 'Synchronous result) -> Eff r result -- | Run a reader effect that contains the one server handling a -- specific Api instance. registerServer :: HasCallStack => Server o -> Eff (ServerReader o : r) a -> Eff r a -- | Get the Server registered with registerServer. whereIsServer :: Member (ServerReader o) e => Eff e (Server o) -- | Like call but take the Server from the reader provided -- by registerServer. callRegistered :: (Typeable reply, ServesApi o r q, HasCallStack, NFData reply, Show reply, Member Interrupts r) => Api o ( 'Synchronous reply) -> Eff r reply -- | Like cast but take the Server from the reader provided -- by registerServer. castRegistered :: (Typeable o, ServesApi o r q, HasCallStack, Member Interrupts r) => Api o 'Asynchronous -> Eff r () -- | Just a wrapper around a function that will be applied to the result of -- a MessageCallbacks StopServer clause, or an -- InterruptReason caught during the execution of receive -- or a MessageCallback data InterruptCallback eff [InterruptCallback] :: (InterruptReason -> Eff eff CallbackResult) -> InterruptCallback eff -- | Helper type class for the return values of spawnApiServer et -- al. class ToServerPids (t :: k) where { type family ServerPids t; } toServerPids :: ToServerPids t => proxy t -> ProcessId -> ServerPids t -- | An existential wrapper around a MessageSelector and a function -- that handles the selected message. The api type parameter is -- a phantom type. -- -- The return value if the handler function is a CallbackResult. data MessageCallback api eff [MessageCallback] :: MessageSelector a -> (a -> Eff eff CallbackResult) -> MessageCallback api eff -- | A command to the server loop started by apiServerLoop. -- Typically returned by a MessageCallback to indicate if the -- server should continue or stop. data CallbackResult -- | Tell the server to keep the server loop running [AwaitNext] :: CallbackResult -- | Tell the server to exit, this will cause apiServerLoop to stop -- handling requests without exiting the process. [StopServer] :: InterruptReason -> CallbackResult -- | Serve an Api in a newly spawned process. spawnApiServer :: forall api eff. (ToServerPids api, HasCallStack) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Serve an Api in a newly spawned -and linked - process. spawnLinkApiServer :: forall api eff. (ToServerPids api, HasCallStack) => MessageCallback api (InterruptableProcess eff) -> InterruptCallback (ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; the callbacks -- have access to some state initialed by the function in the first -- parameter. spawnApiServerStateful :: forall api eff state. (HasCallStack, ToServerPids api) => Eff (InterruptableProcess eff) state -> MessageCallback api (State state : InterruptableProcess eff) -> InterruptCallback (State state : ConsProcess eff) -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. spawnApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | Server an Api in a newly spawned process; The caller -- provides an effect handler for arbitrary effects used by the server -- callbacks. Links to the calling process like linkProcess would. spawnLinkApiServerEffectful :: forall api eff serverEff. (HasCallStack, ToServerPids api, Member Interrupts serverEff, SetMember Process (Process eff) serverEff) => (forall b. Eff serverEff b -> Eff (InterruptableProcess eff) b) -> MessageCallback api serverEff -> InterruptCallback serverEff -> Eff (InterruptableProcess eff) (ServerPids api) -- | A smart constructor for MessageCallbacks handleMessages :: forall eff a. (HasCallStack, NFData a, Typeable a) => (a -> Eff eff CallbackResult) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleSelectedMessages :: forall eff a. HasCallStack => MessageSelector a -> (a -> Eff eff CallbackResult) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleAnyMessages :: forall eff. HasCallStack => (Dynamic -> Eff eff CallbackResult) -> MessageCallback '[] eff -- | A smart constructor for MessageCallbacks handleCasts :: forall api eff. (HasCallStack, Typeable api, Typeable (Api api 'Asynchronous)) => (Api api 'Asynchronous -> Eff eff CallbackResult) -> MessageCallback api eff -- | A smart constructor for MessageCallbacks -- --

Example

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

Example

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