never executed always true always false
1 -- | This module contains a type class that
2 -- describes exchangable operations on messages
3 -- boxes.
4 module UnliftIO.MessageBox.Class
5 ( IsMessageBoxArg (..),
6 IsMessageBox (..),
7 IsInput (..),
8 handleMessage,
9 )
10 where
11
12 import Data.Kind (Type)
13 import UnliftIO.MessageBox.Util.Future (Future, awaitFuture)
14 import UnliftIO (MonadUnliftIO, timeout)
15 import Control.Monad (void)
16
17 -- | Types that configure and allow the creation of a 'MessageBox'.
18 --
19 -- Create 'IsMessageBox' instances from a parameter.
20 -- Types that determine 'MessageBox' values.
21 --
22 -- For a limited message box this might be the limit of
23 -- the message queue.
24 class
25 (IsMessageBox (MessageBox argument), IsInput (Input (MessageBox argument))) =>
26 IsMessageBoxArg argument
27 where
28 -- | The message box that can be created from the
29 -- message box argument
30 type MessageBox argument :: Type -> Type
31
32 -- | Return a message limit.
33 --
34 -- NOTE: This method was added for unit tests.
35 -- Although the method is totally valid, it
36 -- might not be super useful in production code.
37 -- Also note that the naming follows the rule:
38 -- Reserve short names for entities that are
39 -- used often.
40 getConfiguredMessageLimit :: argument -> Maybe Int
41
42 -- | Create a new @msgBox@ according to the @argument@.
43 -- This is required to receive a message.
44 -- NOTE: Only one process may receive on an msgBox.
45 newMessageBox :: MonadUnliftIO m => argument -> m (MessageBox argument a)
46
47 -- | A type class for msgBox types.
48 -- A common interface for receiving messages.
49 class IsInput (Input box) => IsMessageBox box where
50 -- | Type of the corresponding input
51 type Input box :: Type -> Type
52
53 -- | Receive a message. Take whatever time it takes.
54 -- Return 'Just' the value or 'Nothing' when an error
55 -- occurred.
56 --
57 -- NOTE: Nothing may sporadically be returned, especially
58 -- when there is a lot of load, so please make sure to
59 -- build your application in such a way, that it
60 -- anticipates failure.
61 receive :: MonadUnliftIO m => box a -> m (Maybe a)
62
63 -- | Return a 'Future' that can be used to wait for the
64 -- arrival of the next message.
65 -- NOTE: Each future value represents the next slot in the queue
66 -- so one future corresponds to exactly that message (should it arrive)
67 -- and if that future value is dropped, that message will be lost!
68 tryReceive :: MonadUnliftIO m => box a -> m (Future a)
69
70 -- | Wait for an incoming message or return Nothing.
71 --
72 -- The default implementation uses 'tryReceive' to get a
73 -- 'Future' on which 'awaitFuture' inside a 'timeout' is called.
74 --
75 -- Instances might override this with more performant implementations
76 -- especially non-blocking Unagi channel based implementation.
77 --
78 -- NOTE: Nothing may sporadically be returned, especially
79 -- when there is a lot of load, so please make sure to
80 -- build your application in such a way, that it
81 -- anticipates failure.
82 receiveAfter ::
83 MonadUnliftIO m =>
84 -- | Message box
85 box a ->
86 -- | Time in micro seconds to wait until the
87 -- action is invoked.
88 Int ->
89 m (Maybe a)
90 receiveAfter !mbox !t =
91 tryReceive mbox >>= timeout t . awaitFuture
92
93 -- | Create a new @input@ that enqueus messages,
94 -- which are received by the @box@
95 newInput :: MonadUnliftIO m => box a -> m (Input box a)
96
97 -- | A type class for input types.
98 -- A common interface for delivering messages.
99 class IsInput input where
100 -- | Send a message. Take whatever time it takes.
101 -- Depending on the implementation, this might
102 -- be a non-blocking operation.
103 -- Return if the operation was successful.
104 --
105 -- NOTE: @False@ may sporadically be returned, especially
106 -- when there is a lot of load, so please make sure to
107 -- build your application in such a way, that it
108 -- anticipates failure.
109 deliver :: MonadUnliftIO m => input a -> a -> m Bool
110 -- | See 'deliver' but with @()@ as return value.
111 -- If 'deliver' fails, it fails silently.
112 deliver_ :: MonadUnliftIO m => input a -> a -> m ()
113 deliver_ i a = void (deliver i a)
114
115 -- ** Utility Functions for Receiving Messages
116
117 -- | Receive a message and apply a function to it.
118 handleMessage ::
119 (MonadUnliftIO m, IsMessageBox box) =>
120 box message ->
121 (message -> m b) ->
122 m (Maybe b)
123 handleMessage !box !onMessage = do
124 !maybeMessage <- receive box
125 case maybeMessage of
126 Nothing -> pure Nothing
127 Just !message -> do
128 Just <$> onMessage message