Rethinking Programming For The 2020s
2025-02-15
We have the luxury of reconsidering what “efficiency” means, given the massive increases in programming power that have occurred since the early days of programming.
Our computers are much, much faster today than they were in the 1950s.
“... modern computers like the Apple M3 are about 20,000-30,000 times faster than early 1980s computers like the VAX-11/780 and IBM PC ...”.
In the early days, it seemed to make sense to think of “efficiency” in only one way - the efficiency of the final, end-user computer. The trade-off was that it was considered better to expend developer time to get better end-user computer performance.
Today, we have the luxury of thinking of two kinds of efficiency, instead of just one:
End-user computer efficiency
Developer time.
Yet, today, we still use the same caveman tools invented in the early days, i.e. textual programming languages used as IDEs for programming, and we continue to emphasize computer efficiency despite the fact that this emphasis deflects programmers from thinking about other kinds of solutions. There is a flow to program design. Over-thinking efficiency and optimization breaks flow and stunts programmers’ abilities to invent better, different solutions.
In the early days, it was anathema to use strings of characters in a profligate manner, because of the massive difference in speed of operation. In the early days, it was anathema to use backtracking techniques. In the early days, it was anathema to use garbage collectors. In the early days, it was impossible to imagine the use of more than a single CPU.
Today, though, we can use all of the above devices to help speed up development time and to increase inventiveness by not breaking flow states during design.
The analysis, performed by prompting an LLM (Claude 3.5 Sonnet) is included in an appendix of a separate book with the same title as this article. The analysis is not included here.
IDE means Integrated Development Environment. We primarily use synchronous, textual programming languages supported by step-by-step debuggers and syntax colouring. The fact that CPUs are sequenced by instructions has impacted our notion of what higher level languages should be, i.e. sequential, line-oriented texts.
Flow is also called “in the zone”.
The computer science community developed many various techniques and algorithms based on early biases about efficiency, for example:
Various kinds of garbage collection, like reference-counting and generational GC, etc., instead of simply using the biblical flood method.
Time-sharing. It was considered necessary to portion out slices of time on the same computer to many different programs, in order to reduce CPU acquisition costs. This had the ironic effect of actually decreasing the potential efficiency of single programs. This technique has evolved into what is called “concurrency” today with the attendant result that distributed programming is difficult with this mind-set. Concurrency is a simple concept, yet, appears to be difficult to program correctly. This doesn’t need to the case.
Memory sharing. Certain programs - like graphics - require bulk-sharing of data. It was chosen to hard-wire bulk-sharing capabilities into every program and to make every program pay for the use of bulk-sharing even when this is not needed by the programs in question. The cost of bulk-sharing emerged as difficulties in devising correct algorithms, for example considerations for “thread safety”. A very expensive difficulty revealed itself in the crash of the software of the Mars Pathfinder mission.
Tokenization for compiling. The first pass in even modern compilers is called a scanner. The scanner ingests strings of characters that make up a program, then rewrites them as binary integers and data structure objects. The idea is that fixed-width integers are more efficient to process at the machine level than variable-length strings made up of binary characters. The idea of scanners is to perform string scanning only once, then to allow downstream passes to deal only with more efficient fixed-width items. This approach makes compiler-writing into methodical, water-fall projects that take many days/weeks/months/years to complete. Today, programmers can easily write programs that compare strings and programmers can “fool around” iteratively with different language designs before committing to a single design and before spending time on the chosen design. Programmers have the luxury of being able to kick the can down the road and to optimize later, instead of clouding up their flow state during design and experimentation with considerations for too-early anticipation of low-level efficiency. Programmers could be writing string-to-string transpilers to quickly explore ideas instead of optimizing too early and designing overly-rigid solutions that cast in concrete their earliest ideas about problems even when their understanding evolves. Programmers, using compilers and rigid type systems, tend not to go back to the drawing board when they discover new aspects of problems they are solving, due to the time investments they’ve made up to a given point in a design.
Compilation instead of transpilation. Ironically, compilers are transpilers - they transpile high-level textual code into low-level assembler code. There is no need to laboriously build compilers for experimental programming languages just to try ideas out.
Declaration before use. Early ideas about efficiency made it necessary to build compilers that worked in a “single pass” and led to the concept of “declaration before use”. Some modern languages are breaking out of this mindset, but, programmers are saddled with a plethora of languages that insist on declaration before use.
Static languages instead of dynamic languages. The strive for end-user efficiency forced programming languages to be designed in ways that could be AOT (Ahead Of Time) compiled instead of JIT (Just In Time) compiled, even though the basic principles for JIT were budding in the 1970s. In my direct experience, I saw that Utah Lisp contained the concept of fast-calls and slow-calls in the runtime. During the first call to a function, Lisp would use dynamic lookup to find a given function, but, would fix up the reference (by rewriting a memory cell) to make subsequent calls more efficient. The Self programming language further explored techniques for JIT compilation.
Classes instead of prototypal inheritance. Classes are the waterfall-method description of data, as opposed to the more free-thinking prototypal inheritance method for describing data. Waterfall methods tend to break flow state during design.
ASCII instead of Unicode. Just about all popular programming languages are based on 1950s concepts of characters inspired by Gutenberg type setting. ASCII used 7 bits to represent characters. EBCDIC used 8 bits. Many programming languages have stunted capabilities due to the shallow pool of characters used for choosing syntactic constructs. Today, we have Unicode and a much broader pool of characters to choose from. In fact, our hardware no longer supports only fixed-size, non-overlapping grids of small bitmaps, but, can handle vector graphics and 4D graphics (x/y/z/t). I know of no programming languages (programming notations) that use anything but characters. Most languages I am aware of still use ASCII characters in this modern age. For example, strings in most programming languages, use the same character (") for open-quotes as for close-quotes. Strings, therefore, cannot be recursively nested without the use of elaborate techniques. This simple difference makes it difficult to imagine and invent transpilers which would speed up development time.
Functional programming. Functional programming is based on the outdated concepts of using Gutenberg type-setting to express mathematical equations using quills and papyrus. The functional programming paradigm is being pressed into service to handle situations that the notation was not designed for, e.g. problems involving time and sequencing like internet, robotics, GUIs and mouse handling, IoT, blockchain state machine algorithms, etc.
REPLs are thought to be necessarily embedded in programming languages, themselves, instead of being broken out into multiple windows and processes in IDE development machines.
etc.
Biblical flood garbage collection is how the UNIX operating system recycles memory cells. A program runs and uses memory as it wants. When the program dies, UNIX effectively wipes out all memory and allows it to be reused by the next program.
Blockchain programs consist of two main portions: (1) cryptography (2) sequencing state machines for thwarting malicious attacks.
See Also
References: https://guitarvydas.github.io/2024/01/06/References.html
Blog: guitarvydas.github.io
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/65YZUh6Jpq
Leanpub: [WIP] https://leanpub.com/u/paul-tarvydas
Gumroad: tarvydas.gumroad.com
Twitter: @paul_tarvydas
Substack: paultarvydas.substack.com

