-
Notifications
You must be signed in to change notification settings - Fork 3
Design Ideas
Note: this page was used to write down some initial ideas. Given further experimentation/coding, some of it may no longer be correct/relevant.
-
There's no intent to work cross-node. This library allows to build actor-style applications within a single OS process.
-
A process (a.k.a. actor) is a schedulable unit of work (think thread) runs in a
ProcessM
monad which is layered on top ofIO
. -
ProcessM
is not a monad transformer, it's the base monad for any process. -
ProcessM
provides instances of the basic classes (Functor
,Applicative
,Monad
,MonadFail
,MonadIO
,...), as well as various classes for whichIO
implementations exist, e.g.,MonadThrow
/MonadCatch
/MonadMask
,MonadRandom
/MonadSplit StdGen
and others. To investigate:MonadUnliftIO
(if this makes sense),MonadLogger
,MonadManaged
(frommanaged
),MonadTime
(frommonad-time
),MonadLog
(fromlogging-effect
),... -
The primitive actions needed to implement higher-level functionality are provided by a
MonadProcess
class whichProcessM
implements. Every primitive action gets adefault
implementation which lifts it throughMonadTrans
(alternatively,Generically1
could be used, though currently I don't know how). Given that, alltransformers
(thinkReaderT
,StateT
,...) getMonadProcess
instances. Given that, one can easily layer transformers overProcessM
.Part of the rationale here is that this allows to implement e.g., the Erlang per-process dictionary functionality by simply layering
StateT
overProcessM
in some application. -
Semantics and operations on processes are those of Erlang. Hence, processes can exchange messages, and send/receive signals (think exceptions).
-
Alike Erlang, processes can be linked and monitored.
-
Any cross-process interactions are handled in
STM
, except for asynchronous exception delivery where applicable. ThisSTM
interface is somewhat exposed, such that aProcessM
can, e.g.,receiveSTM
some message and update some (shared,STM
) data within a single transaction. -
All exception handling happens through the
safe-exceptions
library. In essence, this should only be used forbracket
-style resource handling anyway: in general, processes should die when exceptions occur. -
It likely makes sense to use
async
andAsync
s to create and manage the process threads. -
Messages that can be sent to a process are not type-bound, similar to
distributed-process
. The implementation can useDynamic
as the type of exchanged messages, unlike theMessage
(with fingerprint) type found indistributed-process
. Other than that, implementation of the mailbox, includingmatch
infrastructure, can be very similar. If desired, type-restricted interfaces (e.g., a typedgen_server
) can be easily layered on top. -
Messages are
NFData
and are fully evaluated within the sender before being added into the target process' mailbox. -
The core functionality is kept as simple as possible. The library does provide higher-level constructs, like a process registry (not using
String
s/atoms as identifiers, but an ADT with constructors specific to the application), behviours likegen_server
,supervisor
,gen_tcp
and others.gen_tcp
and similar may not make sense, GHC has good support for IO/networking, and there's nothing wrong with using plain sockets in aProcessM
, letting the process die upon socket errors/disconnect/..., whatever seems applicable. -
Could consider abstracting all concurrency/communication/IO actions using
io-classes
so the implementation, or higher-level applications, can be tested usingio-sim
. -
Unlike Erlang, we could consider having processes with bounded mailboxes, either blocking (i.e., block a sender when the mailbox is full), or lossy non-blocking, either dropping the last message (or throw an exception in the sender), or drop old messages.
-
The system exposes metrics using the
OpenMetrics
(Prometheus) format. Metrics can include number of actors, mailbox sizes, message delivery latencies,... -
Actors can have a name, which shows up in metrics.
- A bit like how it's done in
Drama
, agen_server
implementation could enforce the use of some GADT as the message type which can either be used forcast
s (type parameter is()
), orcall
(type parameter can be anything, and specifies the return type of thecall
). - Have an easy way to expose a
gen_server
over the network, using CBOR serialization (theserialise
package) for the messages, as well as a client. Given a message typem resp
, this can becast
ed (iffresp
is()
) andcall
ed over a TCP socket to the server using a JSON-RPC-like protocol.