I conclude that we want Software Components that appear to have multiple input ports. At the implementation level, though, there is only one input queue - not one queue per port. Similarly for output ports.
For example
In this diagram, the “Transpile” component has only one input queue and all input ports feed into the same input queue. Input events are queued up in order of arrival and tagged with the port name they came in on.
I believe that dealing with inputs (and input order) is an Architectural decision and should not be built into our tools under-the-hood. Components can have multiple input ports. The tool only guarantees that the inputs are queued up in order of arrival. The Architect decides what to do about the inputs.
Further... multi-processing is essentially a fiction in FP. FP does have concurrency but not true, asynchronous parallelism. Concurrency in FP is performed by time-sharing which only looks like it's running in parallel, because it happens fast enough to fool us humans. Function-based programming metes out a bit of time for one process, then pulls the rug out from under the process, then metes out a bit of time to another process, and so on. Multi-core is slightly faster but still creates accidental complexity by tangling memory sharing and "thread safety" into the issue (as does single-core multi-processing). Multi-processing using shared-memory-by-default and a synchronous (step-wise) approach ain't reality. FP tries to alter reality (aka “simplifying assumption”) by eliding and disregarding ordering. Reality, though, consists of a bunch of distributed machines nattering away at each other at "random". My 0D[1] stuff is only slightly better - it still only simulates parallelism on a single computer, but, gets rid of the memory-sharing-by-default (thread safety) issues, thereby simplifying the whole mess.
Further: there is exactly one "race condition" in reality. Every other kind of race condition is self-inflicted, accidental complexity requiring "workarounds" (aka "epicycles").
THE race condition is: two inputs arrive at the same time. The fixes:
use faster and faster hardware which can sample incoming events in finer and finer time-windows, or,
use state machines to sort out how the code wishes to deal with the apparent simultaneous arrival of events.
Can one “reason about” this kind of thing in a fairly expressive manner? I think yes.
Firstly, we just need a notation that allows us to express (and program) this kind of thing. Statecharts[2] are pretty good, but, we can do even better.
Secondly, when we have invented a suitable notation, we can dive deeper and analyze what works and what doesn’t.
Corollary: FP is, obviously, pretty good, but it’s simplifying assumption restricts thinking along the above lines. A different notation - layered on top of, or, beside FP - can help us deal with multiple input events that come at “random” times. I think that we need to dig deeper than we have been thinking and that we need to deal with issues of “random” events and event ordering. I don’t think that forcing these issues into a functional paradigm is a useful / expressive approach.
See Also
Leanpub [WIP]
Twitter: @paul_tarvydas
Bibliography
[1] 0D from https://github.com/guitarvydas/0D
[2] StateCharts from https://guitarvydas.github.io/2023/11/27/Statecharts-Papers-We-Love-Video.html