It's all about messages and pattern matching
My inability to cleanly insert an intercepting actor to spawn new actors on demand discussed in my previous post about Calculon illustrated tunnelvision in my expression based messages. While these are a great way to express the message contract, they are finally just one way of defining message payloads. The pipeline should be ignorant of message payloads and be able to route any payload based on meta-data alone.
I had to stop hiding the IMessage format under hood and just accept that ExpressionTransport really represents a convenience extension for generating message payloads. This change not only makes it possible for any actor to accept any kind of message, but also to makes the routing a lot simpler and flexible. Now there is only a single, much simpler ITransport:
I've also attached ther sender onto the transport, which means there's one less dependency to inject. Given this, ExpressionTransport and MessageTransport simply becomes extension methods on ITransport:
This provides a lot more flexibility for messaging, since new payloads can be created by implementing
IMessage. It does mean that dynamic dispatch would be nice for dispatching the different payloads against their respective receivers. Oh well, we can work around that.
Determining what messages to accept
Now it's possible for an actor not of type
TRecipient to receive an
ExpressionMessage<TRecipient>, perform some action and re-dispatch it via
Send(). By default routing is done by Id, but missing a direct receiver we can now insert additional pattern matching to determine a suitable recipient. For this I've created an interface to let actors expose their accept criteria:
While this could have been done by convention, I couldn't think of a reason other than current fashion to not use an interface contract to expose this actor capability, so an interface it was. The reason it's an Expression and operates on the MessageMeta rather than the expression is to facilitate serialization of the criteria for delivery across the process boundary so messages can be qualified for acceptance before attempting to cross that boundary.
I now have everything I need to switch the notify.me bots to Calculon. Once that is done, and working reliably, it'll be time to improve the dispatch plumbing to improve speed and reduce concurrency bottlenecks in dispatch.