Message Passing
Message Passing uses FIFOs (queues), CALL/RETURN uses LIFOs (stack).
The State Explosion Problem
A non-hierarchical, "flat", design results in too much non-elided detail.
State Explosion is discussed further in this article.
Structured Message Passing
Structured message passing mimics business ORG charts. Information can only flow "down" or "up". Information must not be sent "sideways".
This is the same structure used in successful business organizations. Sending commands to Components down and around intervening managers is called "micro-management". Sending information (summaries) up and around intervening managers is called "going over the boss' head".
Components must not send messages to their peers. Components must only leave messages on their own output queues. Components' containers (Choreographer Parts1) perform the actual routing of messages to other child Components. Once all message routing within a single container has subsided, the Container produces output which is used by the Container’s Container to pass to other of its own children or is used as its own output. The actual “work” is performed by a Container’s children, the final result of all of the work is passed up the tree as a result. The result is a summary of the work performed internally by the children of a Container.
Structuring message passing in this down/up manner ensures scalability. Any node in the hierarchy can be replaced by a more complicated hierarchy of nodes.
Message passing that relies on a non-hierarchical structure, though, fails due to the state explosion problem. Message passing in a network arranged in a flat manner will fail to scale.
Optimizations flatten hierarchical structure for the sake of efficiency, hence, optimizations must be used with care.
Software Architects construct and debug Designs in a hierarchical manner. Then, Production Engineers flatten parts of the hierarchy for the sake of optimization, and, must check / test for conformance to the original design semantics.
All Components are constructed in a 0D - zero dependency - manner. 0D Components cannot hard-wire the names of other Components into their code. All message-routing decisions are left to the parents (Containers) of Components.
Components are completely isolated / encapsulated. A component template has a signature that is composed of
its name
its set of input ports, and,
its set of output ports.
An instantiated component uses the template as its prototype, but, has a unique name and has one unique input queue and one unique output queue. Parts allow for multiple inputs but append incoming inputs onto a single input queue, tagged with a port name, in order of arrival. Likewise, outputs are tagged and placed on a single output queue in order of sending. The other approach, of providing one queue per port invites deadlock issues. The issue of deadlock does not disappear with the single-queue approach, but handling deadlock is left up to the architect instead of being buried in the lower levels of the kernel in a generalized way that satisfies the union of all possible causes and solutions. I believe that software architects and programmers should solve problems in ways that are specific to a problem-at-hand. This single-queue approach allows efficient solutions to be constructed for each problem, instead of for an array of generalized problems. In analogy, a building architect does not design every building in the same way, but chooses the best way to design a building given specific circumstances. Building architects know of many possible choices (through experience or through education) and make informed decisions about which choice to make, but, don’t choose all possibilities for use within a single design.
In software components, control flow is entirely isolated. A component cannot transfer control flow by calling functions in another component. Components can only ingest messages and produce messages. Components cannot call functions outside of their own boundaries. Components can call functions that they own, but, they cannot call functions that other components own. At best, Components can produce output messages and leave them - in order of production - on their own output queues.3
See Also
References: https://guitarvydas.github.io/2024/01/06/References.html
Blog: guitarvydas.github.io
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/65YZUh6Jpq
Leanpub: [WIP] https://leanpub.com/u/paul-tarvydas
Gumroad: tarvydas.gumroad.com
Twitter: @paul_tarvydas
Substack: paultarvydas.substack.com
This article was originally written in 2022. I used the words “Component”, “Container” and “Leaf”. I use different words, today in 2025, to describe the very same concepts - “Part”, “Choreographer” and “Worker”, respectively.