Async

The default should be that everything is asynchronous.


Synchrony should be the exception, not the rule.


No Loops

Loops and recursion, currently, imply synchrony.


Loops are replaced[1] by:


See https://guitarvydas.github.io/2021/05/05/Breaking-Long-Running-Loops.html


Sync

Functions/operations that are synchronous must be prefixed by the sync keyword.


Sequencing is not the Default

In most current PLs, operations follow a strict sequence specified by consecutive lines of code or nested function calls.


For example


fn(5)

fn(6)

and

fnx(fny(7))


implies that fn(6) is invoked only after fn(5) has been invoked and has finished.  Likewise, fnx() is called only after fny() has finished. 


In the first case, the language has a hidden synchronization operation and in the second case, fnx() has a hidden wait() operation built into it[3].


Computers are able to perform concurrent operations, but our PLs build synchronization in as the default[4].


Distributed computers don't necessarily act as functions, but act as components[5].


Mathematics is a notation for dealing with functions that are implicitly synchronized.  Using mathematical notation for programming languages limits what can be done[6] with a computer.  For example, programmers imagine that distributed programming is "hard" and tend not to invent languages that span processors, e.g. inventing a language that deals with 2 rPis instead of only one rPi at a time ; currently we have only libraries of assembler-like operations to deal with multiple processes and multiple CPUs[7] and some languages attempt conflate expression of so-called parallel processes into a functional notation.

What Happens When Functions Aren't Synchronous?

Q: In the case


fnx(fny(7))


what happens if fnx() does not employ a built-in wait operation?


A: Maybe fnx() runs before fny(). (!)


Components run forever.


When an Architect wants a component to wait for input from another component, the Architect must specify that this is the case.


Q: Does that mean more code?


A: Not if an appropriate notation is employed.


Currently, our notations specify synchronization.


See Relational Programming.



Calculators vs. Programming

Current programming make it easy to express calculator style code.


E.G. input -> perform calculation -> output.


Even for this simplistic paradigm, we have added a syntactic bag called exceptions.  Exceptions break the fundamental tennet of Structured Programming


Building a calculator should be the exception, not the rule.



Programming can go well beyond the notion of just building calculators.


https://vimeo.com/113707214



Calculator Paradigm

The calculator paradigm assumes that a program has


Pros

Cons

Examples

Anti-Examples



Relational Programming

Relational Programming (aka declarative programming) is backing in to the idea that code should not specify synchronization.  In relational languages, one writes relations that must be satisfied — in any order — for a rule to succeed.  The engine decides on the ordering of relation evaluation.


Asynchronous Languages

An asynchronous language does not have implicit synchronization built into its syntax.


An example is relational programming.


A language for asynchronous components allows the programmer to specify:


Inputs do not necessarily "happen at the same time", as is the case with current programming languages (parameters are all delivered at the same time when a function is called).  [If a component needs to wait for a set of inputs to be ready, then the Architect must explicitly say so.]


Outputs do not necessarily "happen at the same time".  A component can produce an output at any time.  


Current PLs return all values "at the same time".  A function returns its values when it returns.  This is a form of synchronization.  [In contrast, consider a server.  It waits for requests and responds when it is good and ready.  It could respond more than once — there is nothing stopping it from doing so — but, our mindset occludes this possibility and considers it to be an error.]


Q: Does the Architect need to be explicit about synchronization?


A: Yes.



Q: What work can be done asynchronously?


A: All sorts of work that we can't even imagine when using the current breed of synchronous languages.  For an analogy, consider the human body.  The human body has an autonomous system that involves some 500 concurrent processes, but, the human body has only one thread of consciousness.  In our current mindset, we would program this by building 500 calculators and shoving them into envelopes (called "threads") with point-to-point connections for inter-envelope messaging.



Q: Does any of the above look familiar?


A: Yes.  Networking, internet, distributed computing, etc. is like this.  Note that GUI's are like this, too.  User input — keyboard, mouse, etc. — comes "at any time" and there must be a component ready to accept such input.  Currently, we fake it and force-fit the everything-is-a-function paradigm onto these kinds of problems.  We can solve these problems in the current paradigm, but they could be solved more easily using other paradigms.  Our current solution — using an inappropriate paradigm — have been causing accidental complexities.[13]



[1] Compilers could do this replacement.

[2] To signal next iteration step.

[3] Specifically, fnx() uses the CALL low-level opcode and fny() uses the RETURN low-level opcode to signal that it has finished.  The synchronization is performed by the underlying hardware, using a global variable (the Stack).

[4] So, by default, we do not get concurrency.

[5] A component is like an Actor that isn't synchronized by default.

[6] easily

[7] Note that we talk about CPU - centralized processing units - even when discussing distributed computing.

[8] https://vimeo.com/113707214 shows an interesting way to organize one-in-two-out programs.

[9] Substitution works only if (a) there are no side effects or (b) components are well-isolated and all side-effects do not leak beyond the boundaries of an isolated component.

[10] Typed languages with pattern matching are attempting to rein this in.  Such languages make it possible to create two types — result and error — instead of creating two outputs.  I argue that it would be simpler to admit that components can have more than one output port.

[11] I consider "pencil and paper" to be a 1D notation.  Actually, it deals with grids of non-overlapping characters arranged in rows, which is 2D-ish.  I argue that we should deal with grids of overlappable elements arranged in (x,y) space instead of in (row,line) cell space.

[12] Ilya Prigogene (Nobel laureate), "Order Out Of Chaos"

[13] Such as priority inversion, full preemption, thread safety, etc.