Computer software deals with two kinds of information:
Separation of Concerns
See S/SL (in References) for an example of a data-less language.
Locality of Reference
Information leaks are currently called “dependencies”[^locality].
An insidious form of information leak is the use of functions!
In computer science, it has often been the case that problems have been solved through the use of nesting - aka scoping.
Structured programming prescribes nesting of control-flow as a solution to the problem of spaghetti control-flow.
The problem of global variables was solved using nesting.
The “real” problem is one of spaghetti dependencies. How to stop programs from becoming “too large”?
Object Oriented Programming
OOP is a solution for structuring data and divorcing data from implementation details.
Inheritance is a useful way to organize data.
Inheritance is a poor way to organize code.
Syntax is, currently, used for dealing with control-flow descriptions.
Two languages for any program -
- a data description language
- a control-flow description language
The trend in FP is to use pattern matching to separate control information from data information.
Pattern matching is well-understood, albeit under a different name - “parsing”.
Denotational Semantics is the field of describing programming languages using functional notation.
Control-flow, in Denotational Semantics, is most often handled through the use of
GOTOs (rebranded as CPS, first-class-functions, continuations, etc.).
“Data flow” refers to a style of control-flow, not to data structuring.
FP requires that all inputs arrive at a component (a function) at the same time. FP assumes that there is a single happy path.
The current model for control-flow - syntax - is based on assumptions related to 1950’s computer hardware - e.g. a single CPU[^cpu] and expensive memory.
Deep Recursion, Long-Running Loops
Programmers conflate the various uses of state and lump them together.
I discuss this further in https://guitarvydas.github.io/2021/03/30/State,-Analysis-of.html.
I discuss StateCharts further in https://guitarvydas.github.io/2020/12/09/StateCharts.html and https://guitarvydas.github.io/2021/02/25/statecharts-(again).html.
Note that many “successes” in programming have been built on top of the state paradigm, e.g. operating systems, YACC, LEX, REGEXP, etc.
Threads are more like assembly-level operations provided on a single CPU than a high-level concept useful for programming distributed systems.
See the References section for S/SL - a dataless language that was, ostensibly, used for constructing compilers (aka “big” programs).
The problem with GOTOs was not the GOTOs themselves, but the unstructured use of GOTOs.
Components are scalable only if they are not inter-related.
Scalable components cannot have dependencies on one another.
Currently, most PLs provide a handful of hard-wired types and a way for programmers to define further types.
The fact that three forms of type checking exist is a tell that the concepts of type checking are non-uniform.
Most PLs create names that are absolute and global to the whole application.
At present, I believe that dependencies are the first-order problem.
… dependency debt.
I believe that diagrams show dependencies more readily than textual code.
The Little Things matter.
Software components are asynchronous by default.
Synchronous components are the exception, not the rule.
Software components “live forever” (like web servers).
Software components can be supplied inputs at different points in their lifetimes.
Software components can produce outputs at various points in their lifetimes.
Exceptions are not exceptional.
Exceptions produced by components are the same as all other outcomes produced by components.
The problems and the solutions dictate which outcomes are considered to be erroneous. Software Architects design solutions that produce the desired outcomes, picking from a multitude of choices. Few problems have exactly one solution - it is the Architects’ responsibility to make vaarious trade-offs and to make the design clear to readers.
Software components have input and output ports.
Most current PLs have APIs that imply synchronous operation.
One Universal Type
Components are plugged together port-to-port where ports have a single, universal, simple type, e.g. a message.
Types checking is done in a pipeline, from simple to more complex.
Not all software components need to fit this simple - one-in-two-out - model.
Components are built in layers.
No layer contains more units than can be comprehended, e.g. 7±2.
Components can, themselves, contain layers, recursively.
Long running loops and deep recursion are not allowed.
Diagrams are a way to visualize multiple outcomes.
Diagrams are a way to show nesting and locality of reference.
Diagrams can visualize information leakage.
Diagrams make it difficult to draw leaky components, especially when everything (e.g. function calls) is made explicit.
Example Diagram Scenario
A software component is represented as a box.
Software components are asynchronous.
Lines represent message flow paths.
Software components contain input and output ports.
Input ports are small green circles.
Output ports are small yellow circles.
A Dispatcher routine invokes ready components in a random order.
All names are relative to components.
Components have 5 namespaces:
connections between components
A component refers to a component that is contained in it by using a name (e.g. “inner”) or and index (1, 2, 3, …), for example:
Likewise, it can refer to a named input “in” as, for example: