The pub/sub model captures a simple concept - assign "handlers" to an event manager of some kind, and delegate messages to those handlers by issuing events.
This library is an implementation of a trivial pub/sub model for PureScript:
import Queue (Queue, READ, WRITE, newQueue, onQueue, putQueue) type Effects eff = (console :: CONSOLE | eff) main :: forall eff. Eff (Effects eff) Unit main = do (q :: Queue (read :: READ, write :: WRITE) (Effects eff) Int) <- newQueue onQueue (readOnly q) \x -> logShow x putQueue (writeOnly q) 1 putQueue q 2
The calls to
writeOnlyaren't necessary; they're just to demonstrate the ability to quarantine sections of your code which you only which to expose
(read :: READ)or
(write :: WRITE)as the facilities available to the queue.
It tries to immitate similar functionality to
Chans from Haskell, but isn't nearly as cool.
We get something similar to it; blocking when data is available, using the
and its siblings.
Queue module provides a set-like perspective on handlers (you can additively register individual event
handlers, but can only clear them all at once), while the
IxQueue module treats handlers
as an indexed mapping, allowing you to distinguish which
handler receives a message (opposed to broadcasting to all handlers), or descrepently remove a
single handler from the map (can be useful for dynamic allocation of multiple handlers to the same data).
There's also a
Queue.One module, which has an implementation where there's
at most one handler on the queue at any given time.
The behavior of this library is such that, if no handlers are registered, then store pending messages first-in-first-out until one is available.
Queue is useful if you know the network of handlers is static for the lifetime of their operation,
or at least strictly additive.
IxQueue is useful when you need precice control over the presence of handlers
in a queue at runtime, each of which receive the same data.
Queue.One is a convenient reduction of
where you only need to keep track of a single handler.
This library's main goal was to aid in cross-site interopability - almost localized
RPC mechanisms - through the
Queue.One.Aff modules, we can treat
message passing at a higher level as procedure invocations:
import Queue.One.Aff (newIOQueues, registerSync, callAsync) -- most lightweight implementation import Control.Monad.Aff (runAff_) main = do io <- newIOQueues registerSync io $ \i -> do log $ "input: " <> show i let o = i + 1 log $ "incremented: " <> show o pure o runAff_ logShow $ do result <- callAsync io 20 liftEff $ log $ "Wow that was a super complicated delegated computation! Result: " <> show result
Queue.Aff is useful when you might have multiple
registerSyncOnce calls waiting for the same input,
but clean themselves up, or multiple
registerSync calls, if you know the network statically (deletion clears all of them).
IxQueue.Aff is useful when you need async calls, but need to integrate into an existing
IxQueue of other recipients.
Note that in
IxQueue.Aff, you need to pass the reference returned from
callAsync - it's the
UUID key generated for the pending invocation. An advantage of
Queue.One.Aff, naturally, is they don't need
to be registered before invoked - the input queue will hold on to the data.