Single-Paradigm vs. Multi-Paradigm Thinking (Slight Return)
Managing Programming Complexity 2024-11-09
Multi-Paradigm Programming
Single-paradigm Programming, Towards A Software Stack
Restricted Thinking
When paradigms are stacked on top of a base paradigm, all of the stacked paradigms are funnelled through and restricted by the underlying simplifying assumptions of the base paradigm.
Such restrictions cause brain-power to be wasted on the invention of workarounds (also called “epicycles” in the days of Ptolemaic Cosmology) and tend to prevent free-thinking. This is a similar effect to the way that language affects thought.
If a given solution is forced to fit into an unsuitable paradigm, something has to give. Usually the design space for a solution suffers from the restrictions imposed by the base paradigm substrate.
CPUs are capable of supporting multiple paradigms, not just the function-based programming paradigm. CPUs support only one programming language - assembler. Function-based languages are not indigenous to CPUs. What is commonly thought of as “programming” today is but a single-paradigm approach to problem-solving. Striving to write all solutions to problems in functional form is a kind of restricted thinking.
Stacking paradigms on top of other paradigms leads to further stacking. Today, we use a complicated “software stack” that is, at least in part, due to the penchant for stacking paradigms onto substrate paradigms and on injecting ideas into substrates that don’t actually fit the substrate paradigms.
Where Did the Function-Based Paradigm Originate?
We implemented function-based programming in programming languages as far back as C and Pascal and Algol. Today, the epitome of function-based programming is called FP - Functional Programming.
At some point early in the history of programming languages, we switched from using the word “subroutine” to using the word “function”. This minor wording difference affected our beliefs.
I think that function-based programming came from the desire, in the early days of computer-ing, to apply tried-and-true, Guttenberg typesetting notation for mathematics to new-fangled devices called CPUs. Ian Arawjo[1] explores some of the history of programming language design. James Tanton[2] explores the effect of the Guttenberg press on mathematical notation.
My Preference
Consider the following example. Most popular programming languages support heaps. The truly-pure, function-based paradigm is stack-based, and, does not directly support heaps. As Sector Lisp[3] shows, it is possible to create garbage collectors that are significantly more efficient when sticking to the pure function-based paradigm than when injecting the concept of random-access heaps into the function-based paradigm. Sector Lisp, also, demonstrates that the pure function-based paradigm is not rich enough to express CPU-based operations such as mutation, control flow and random access memory.
Consider another example. Concurrency. True asynchronous concurrency devolves into a configuration of two computers, say Arduinos, joined by a wire. Functional solutions to this kind of configuration involve unrealistic ideas, like memory sharing between the two machines. This ends up complicating matters with issues of thread safety. The machines are joined by a single wire. The machines are isolated. The machines are thread safe by definition. To share memory between them, you need to ship huge amounts of data through the wire. It’s more efficient to simply pass small messages through the wire and to let the machines run at their own speed instead of synchronizing them to a common click-track (which, also, needs to be sent through the wire). It is more realistic to just devise “protocols” between the isolated machines. Humans understand this, and, have been doing this kind of thing for ages, e.g. they use the protocol of shaking hands when meeting someone else, they use the protocol of waiting for the guest speaker to arrive before starting a meeting, etc. Of course, there are problems that require the use of shared memory, e.g. heavy graphics rendering, but, those problems are the exception, not the rule. Most current-day programming languages, though, build the notion of shared memory deep into the bowels of the languages (thread libraries, thread safety, multi-tasking, etc.). This makes every application pay for a solution to an exceptional problem, even when the problem doesn’t need to exist.
Consider yet another example. Ethernet. Was the design of ethernet based on synchronized, functional thinking? Nodes on the ethernet do not synchronize with one another, at all. They use a “randomized back off” strategy when they realize that they are talking over one another. Ethernet appears to work fine without the need for deeper synchronization.
The general approach to creating programming languages has been to inject non-functional ideas into the function-based paradigm and to stretch the paradigm beyond its “sweet spot”. This approach is driven by the desire to create general purpose programming languages. This approach ultimately leads to watering down of programming languages and design approaches in the quest to unify all possible solutions into a single form of expression.
I favour the idea of using multiple programming paradigms and multiple programming languages, each focussed on succinctly expressing the concepts of solitary paradigms. I favour the idea of having programming workflows and IDEs support the concept of composing solutions using multiple paradigms.
Only “toy problems” can be solved using a single paradigm. “Real” problems need to be addressed from several angles using several paradigms.
Problems that we face today tend to involve concepts that are outside of the pure function-based paradigm. For example, internet, robotics, IoT, GUIs, blockchain, etc., all require addressing the concept of asynchronous concurrency - the idea that entities are isolated and distributed and run at their own speed, producing results at “random” times, instead of all running in a lock-step manner and producing results based on a common click-track.
Bibliography
[1] Ian Arawjo. 2020. To Write Code: The Cultural Fabrication of Programming Notation and Practice. In Proceedings of the CHI 2020 April 25-30, 2020, 2020, Honoulu, HI, USA. Association for Computing Machinery, [insert City of Publication],
[2] James Tanton. The Story of the Vinculum from
[3] sector lisp from https://justine.lol/sectorlisp2/
See Also
References: https://guitarvydas.github.io/2024/01/06/References.html
Blog: https://guitarvydas.github.io
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/qtTAdxxU
Leanpub: [WIP] https://leanpub.com/u/paul-tarvydas
Gumroad: https://tarvydas.gumroad.com
Twitter: @paul_tarvydas