In order to enhance human readability and design-ability, programs and code must be constructed and arranged in layers.
I discuss the main issue - structuring code as Components - in this article, and, leave other issues that affect layering - like ports, extreme decoupling, queues, etc. - for other articles.
None of what I discuss is difficult to implement. The idea of Software Components only requires changing one’s viewpoint. For example, component-based programming can be implemented by creating a few simple classes, like queues, and using specific algorithms for data routing.
In a layered system, the topmost layer provides an overview of the code, allowing readers to drill down into sublayers to get more detail about any aspect of the code that they want to know more about.
How many layers should there be?
A Component-Based programming system must allow for an infinite number of layers.
The programming language Lisp shows us how to do this simply. Lisp uses lists that contain only two kinds of things
1. Atoms
2. Lists.
Lists can contain other lists, even lists that contain other lists. You hit rock bottom when you encounter an Atom.
Lists can contain any number of items. Lists can contain a mixture of Atoms and Lists.
How many items is too many?
For machines, any number is OK, limited only by the speed and size constraints of the particular machine(s).
For human comprehension, though, there should be only 7, plus or minus 2, Components in any one layer. See Rule of 7[1]
How Can We Arrange Software Code Units in A Layered Manner?
We need only to define 2 kinds of code units
1. Leaf Components
2. Container Components
Leaf Components are the Atoms of the Component model. Leaf components contain code - which we currently call functions. A Leaf component contains one main entry point which receives a packet of data - a message. Leaf components may be implemented as a bunch of private functions, similar to a file of code in current programming languages. The code file exports only the main entry point. All other functions within a file are private helpers for the main function.
Leaves, also, contain a second entry point - a function or method that instantiates new copies of the Leaf.
Container Components are the Lists of the Component model. Containers contain any number of other components, any mix of Leaves and other Containers.
Containers look like ordinary Components from the outside, but, when Containers receive data / action requests, they punt the work to their immediate children.
Containers compose new "code" by combining their immediate children components into little networks. Containers ensure that one, and only one, input travels through the little network and is processed until all immediate children have reached quiescence. Recursively.
This necessitates that all components have 1 input queue and 1 output queue. Incoming information is queued up - in a FIFO manner (not a LIFO manner like a function call/return sequence) - and tagged with port ID's. Output queues are made available to immediate children and to their descendants.
More details to follow regarding queues, ports, isolation, etc. Suffice it to say that all of these concepts can be easily implemented using current programming languages, using only basic programming techniques. Nothing fancy nor complicated. Just a few lines of code. Just a different way of squinting at what we call code and functions.
See Also
Leanpub [WIP]
Twitter: @paul_tarvydas
Bibliography
[1] Rule of 7 from https://guitarvydas.github.io/2023/04/03/Rule-of-7.html