<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Programming Simplicity]]></title><description><![CDATA[Exploring simplicity in programming]]></description><link>https://programmingsimplicity.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png</url><title>Programming Simplicity</title><link>https://programmingsimplicity.substack.com</link></image><generator>Substack</generator><lastBuildDate>Mon, 20 Apr 2026 02:26:42 GMT</lastBuildDate><atom:link href="https://programmingsimplicity.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Paul Tarvydas]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[programmingsimplicity@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[programmingsimplicity@substack.com]]></itunes:email><itunes:name><![CDATA[Paul Tarvydas]]></itunes:name></itunes:owner><itunes:author><![CDATA[Paul Tarvydas]]></itunes:author><googleplay:owner><![CDATA[programmingsimplicity@substack.com]]></googleplay:owner><googleplay:email><![CDATA[programmingsimplicity@substack.com]]></googleplay:email><googleplay:author><![CDATA[Paul Tarvydas]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Understanding Smallness and Speed]]></title><description><![CDATA[2026-04-15]]></description><link>https://programmingsimplicity.substack.com/p/understanding-smallness-and-speed</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/understanding-smallness-and-speed</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Wed, 15 Apr 2026 14:47:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I feel that modern code is bloated.</p><p>I don&#8217;t know why some code is less bloated than most code.</p><p>I&#8217;ve listed some theories below.</p><p>In the &#8220;scientific method&#8221; a theory is only good if it is falsifiable. What are the falsifying conditions for the theories below? Can we easily create experiments to disprove some of the below theories? &#8220;Science&#8221; is a fail-fast methodology. Someone proposes a falsifiable theory, then others attack the theory and try to falsify it to remove it from consideration asap.</p><p><strong>theory</strong></p><p>the function-based calling convention adds bloat when mechanically applied to all code on a software system</p><p><strong>theory</strong></p><p>forcing <em>all</em> DI (Design Intent) through the functional-paradigm eye-of-the-needle causes refactoring of DI into a more bloatful form (just because you <em>can</em> express a concept in functional form doesn&#8217;t mean thst you <em>should</em> express it that way (e.g. control flow is expressed better as state machines instead of as functions)).</p><p><strong>corollary</strong></p><p>you need to apply many <em>different</em> paradigms to solve any one real-world problem. Forcing oneself to use only one paradigm causes warping of the paradigm and causes a bloatful result</p><p><strong>theory</strong></p><p>increasing opcode size in modern CPUs causes bloat</p><p><strong>factoid</strong></p><p>I once worked on a compiler that emitted 8-bit 6809 code and 16-bit 68000 code. I measured (IIRC) that, for the same program, the 8-bit code was 40% the size of the 16-bit code. So, just going from 8-bit opcodes to 16-bit opcodes cost a 2.5x code size increase.</p><p>[implied experiment: can we keep more &#8220;code&#8221; in private L1 cache by re-encoding a portion of the &#8220;code&#8221; using 8-bit meta-opcodes and writing a little interpreter? Would that improve locality while remaining sufficiently fast? (Which immediately makes me think &#8220;Forth?&#8221;).]</p><p><strong>factoid</strong></p><p>loosely, Lua is 1,250x larger than Sector Lisp. Rust is 750,000x larger than Sector Lisp. <a href="https://programmingsimplicity.substack.com/p/programming-language-sizes?r=1egdky">article</a>. I wonder why this is&#8230;</p><p><strong>factoid</strong></p><p>Turbo Pascal included a compiler and an editor in about 33k bytes and ran on early, 20th century hardware and operating systems.</p><p><strong>theory</strong></p><p><em>something</em> about the construction of Turbo Pascal made the resulting binary massively smaller and faster than modern compilers. <em>Something</em> about the way we write &#8220;modern&#8221; code is resulting in 1000x increase in size and bloat.</p><p><strong>conclusion</strong></p><p>Sector Lisp is not small primarily because it is written in assembler and because it uses assembler tricks. I conclude that Sector Lisp is small because it sticks to one and only one paradigm (functional) and benefits massively from sticking to this paradigm. I think that Sector Lisp is the most pure functional program I&#8217;ve analyzed (one would think that Tunney&#8217;s BLC is even more so, but I haven&#8217;t dug deeply into the code). <a href="https://programmingsimplicity.substack.com/p/is-sector-lisp-small-only-due-to?r=1egdky">article</a>.</p><p><strong>theory</strong></p><p>my feeling, about modern code being bloated, is just wrong</p><p><strong>Further</strong></p><p>I expect to set up a new channel on the &#8220;<a href="https://discord.gg/65YZUh6Jpq">programming simplicity</a>&#8221; discord to act as a running train of thought for falsifying any of the above or for adding new falsifiable theories. I think that with the help of LLMs and brain power, we can dissect the original version of Turbo Pascal (version 1, DOS) and see if that provides any insights. Active participation encouraged.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p><em>Paid subscriptions are a voluntary way to support this work.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/understanding-smallness-and-speed/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/understanding-smallness-and-speed/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/understanding-smallness-and-speed?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/understanding-smallness-and-speed?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Some Thoughts About The Computing Historian Recovering Ideas We Threw Away]]></title><description><![CDATA[2026-04-14]]></description><link>https://programmingsimplicity.substack.com/p/some-thoughts-about-the-computing</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/some-thoughts-about-the-computing</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 14 Apr 2026 14:18:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>Some Thoughts About <a href="https://www.youtube.com/watch?v=_dhsm0PLOk0">The Computing Historian: Recovering Ideas We Threw Away</a></p><ul><li><p>The internet is asynchronous multi-tasking. We already solved it in hardware. Software academia re-complicated it.</p></li><li><p>Design recovery is archaeology &#8212; and we need a meta-language for the dig.</p></li></ul><p><strong>L;R</strong></p><p><strong>We Finally Have It</strong></p><p>The internet is asynchronous multi-tasking. Many independent machines, running at their own speeds, communicating when they choose to.</p><p>This is not a new idea. Hardware has always been asynchronous. Every chip on a PCB runs at its own speed. Every peripheral operates independently. Hardware engineers have been composing asynchronous components for decades. It works.</p><p>Software did not inherit this. Software multi-tasking is a rotary switch &#8212; a scheduler cycling through tasks, one at a time, pretending to be simultaneous. The OS is a software implementation of something a hardware switch does cheaper, faster, and without a growing list of security vulnerabilities.</p><p>L1 caches are beginning to make CPU cores genuinely independent. But this is an overly complicated path to something you can achieve by buying several Arduinos.</p><p><strong>The Concurrency Illusion</strong></p><p>Software inherited &#8220;concurrency&#8221; problems from shared memory. Shared memory is a local decision that became a global constraint. The internet does not share memory. Machines share data &#8212; they send messages, they do not hand off pointers. This is not a coincidence. It is the correct design.</p><p>Every &#8220;concurrency&#8221; problem on the internet is simpler than the equivalent problem in a multi-threaded process, for exactly this reason. The difficulty has been re-introduced artificially by building internet services that internally revert to shared-memory designs.</p><p>The wheel was invented. Then reinvented with a flat side.</p><p><strong>What Academia Optimized For</strong></p><p>Modern computing has been shaped by academic concerns, not UX concerns. The abstractions that dominate &#8212; type systems, garbage collection, monads, effect systems &#8212; are refinements of notation for humans writing programs. They are not refinements of how machines work.</p><p>Casey Muratori&#8217;s <a href="https://www.youtube.com/watch?v=wo84LFzx5nI">The Big OOPS</a> makes this precise: the abstractions that evolved are measurably inefficient for real reprogrammable machines. The gap between the abstraction and the machine has grown to the point where performance requires heroic effort to recover what was given away for free at the start.</p><p>Sketchpad was written in machine code. No type checker. No GC. It worked. It still impresses. The intellectual content was not in the language &#8212; it was in the design. We didn&#8217;t really need 50+ years of incremental complications added to programming languages to use CPUs as building materials.</p><p><strong>Programming vs. Re-Programming</strong></p><p>The fundamental invention of computer science is not &#8220;programming&#8221; itself. The fundamental invention is that of &#8220;re-programming&#8221; &#8212; the study of how to use reprogrammable machines effectively.</p><p>This distinction matters. A reprogrammable machine has GOTO. Removing GOTO from the notation does not remove it from the machine. It removes it from a specific paradigm for thinking about programs. That paradigm &#8212; functional, structured, referentially transparent &#8212; makes re-programming easier within its assumptions. It does not describe reality. It describes a useful fiction layered over reality.</p><p>The fiction has costs. The costs compound &#8212; and today we speak in terms of giga-bytes instead of kilo-bytes.</p><p><strong>Design Recovery</strong></p><p>Old software contains designs. The code that implements them is written for machines that no longer exist, in notations that no longer compile, under assumptions that were never written down.</p><p>Design recovery is the act of lifting a design out of its implementation &#8212; finding the decisions, the structure, the intent &#8212; and re-expressing it for a new machine.</p><p>This is archaeology. The artifact is not the pottery shard. The pottery shard encodes the artifact. The design is what you are after.</p><p>What notation captures a design without committing it to an implementation? Factbases of triples are a candidate &#8212; subject, predicate, object, nothing assumed about execution order or machine model. A design expressed as a factbase is substrate-neutral. It can be queried, transformed, re-emitted into any target notation.</p><p>This is what design recovery requires: a representation that sits above the code, below the marketing diagram, and carries enough structure to reconstruct intent.</p><p>We threw this away when we decided that code <em>was</em> the design.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p><em>Paid subscriptions are a voluntary way to support this work.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/some-thoughts-about-the-computing/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/some-thoughts-about-the-computing/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/some-thoughts-about-the-computing?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/some-thoughts-about-the-computing?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Y2K: When Code Became Archaeology]]></title><description><![CDATA[2026-04-14]]></description><link>https://programmingsimplicity.substack.com/p/y2k-when-code-became-archaeology</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/y2k-when-code-became-archaeology</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 14 Apr 2026 12:36:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>Canadian banks in the late 1990s faced a deadline: fix 20+ million lines of COBOL before January 1, 2000, or risk systemic failure. Traditional software methods couldn&#8217;t meet the timeline. We used design recovery and transitive-closure factbases to hot-spot the problem. LLMs could have helped.</p><p><strong>L;R</strong></p><p>Around 1995, the Y2K problem stopped being theoretical.</p><p>The five major Canadian banks ran on COBOL. Much of that code had been written in an era when memory was expensive and every byte counted. Programmers stored years as two digits &#8212; 95 instead of 1995. Sensible optimization at the time. Time bomb in retrospect.</p><p>The question was not whether the code was broken. The question was <em>where</em>.</p><p>One bank we worked with had 22,000,000 lines of COBOL. Their staff of 200 COBOL programmers could handle, at best, 5% of the codebase per year. Manual inspection of the full codebase was not a viable path to January 2000.</p><p>Re-testing compounded the problem. Banks needed to run month-end simulations. A full re-test of the affected systems would have taken years &#8212; longer than the time remaining. And the banks had to stay live. Five days a week, the systems ran production. Three Saturdays per month were reserved for maintenance windows. Sundays were reserved for rollback &#8212; if a Saturday fix broke something, Sunday was the recovery day. That left one Saturday per month for Y2K remediation attempts. One.</p><p>Traditional methods were a non-starter.</p><p><strong>Design recovery</strong></p><p>Our companies &#8212; DRI and Legasys &#8212; approached the problem differently. We unparsed the COBOL code and constructed factbases: structured repositories of facts about the semantics of the code. Variables, their types, their assignments, their flows through the system. We then ran transitive closures over those factbases to trace how date-like values propagated across the entire banking system.</p><p>The goal was hot-spotting: not to examine everything, but to identify the code that <em>needed</em> examination. Triage by analysis rather than by guesswork.</p><p>It was not clean work. One persistent obstacle: variable naming. Not all variables named like dates were dates. Some variables that matched our date heuristics had been given female names by the original programmers and had nothing to do with calendar values. The factbase picked them up anyway. Every false positive cost time.</p><p>The reverse was also true and harder to detect: date values traveling through variables named for something else entirely.</p><p><strong>What LLMs could have done</strong></p><p>We were doing, essentially, semantic inference from code. We had automated tools, but the tools required human judgment at many points &#8212; deciding whether a variable was semantically a date, whether a computation path warranted investigation, whether a flagged routine was genuinely handling calendar arithmetic or something superficially similar.</p><p>LLMs are good at exactly this kind of disambiguation. Given a block of COBOL with a suspicious variable, an LLM can reason about what the variable likely represents based on its usage context, the surrounding arithmetic, and the names of adjacent variables. It can do this at a scale that human reviewers cannot.</p><p>Our transitive closure gave us a graph. An LLM could have pruned that graph &#8212; classifying nodes, filtering false positives, ranking which paths warranted human attention. The combination of automated structural analysis and language-model semantic reasoning would have been sharper than either alone.</p><p>We didn&#8217;t have that option in 1995. The tools that existed were the tools we used.</p><p>The problem got solved. The banks stayed live. January 1, 2000 passed without systemic failure.</p><p>But the experience left a clear lesson about software at scale: you cannot read what you cannot find, and finding requires inference, not inspection.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p><em>Paid subscriptions are a voluntary way to support this work.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/y2k-when-code-became-archaeology/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/y2k-when-code-became-archaeology/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/y2k-when-code-became-archaeology?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/y2k-when-code-became-archaeology?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[LLMs as Design Recovery Engines]]></title><description><![CDATA[2026-04-14]]></description><link>https://programmingsimplicity.substack.com/p/llms-as-design-recovery-engines</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/llms-as-design-recovery-engines</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 14 Apr 2026 12:19:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong> &#8212; Old code is dying. The code isn&#8217;t the problem. The business rules embedded in it are. LLMs can extract those rules into a factbase. A factbase can be rewritten into any target language. This is Design Recovery, and it is the right job for LLMs.</p><p><strong>L;R</strong></p><p>Paul Rony, in the interview &#8220;<a href="https://www.youtube.com/watch?v=_dhsm0PLOk0">The Computing Historian Recovering Ideas We Threw Away</a>,&#8221; raises an alarm: old code stops running on new machines. The surface-level fix is emulation or virtualization. But that isn&#8217;t the real problem. The real problem is that the <em>logic</em> inside the code &#8212; the business rules, the domain knowledge, the decisions baked into conditionals and data shapes &#8212; is entombed in a language or runtime that is becoming inaccessible.</p><p>This is a portability problem. Compilers have grappled with portability for decades. The standard hack is #ifdef and conditional compilation. It works at the syntax level. It does not work at the semantic level. #ifdef lets you swap one system call for another. It does not let you understand <em>why</em> the original code made the choices it did.</p><p>Understanding why is Design Recovery.</p><p>Design Recovery is a field. It is underutilized. Its goal is to extract the abstract logic from a concrete implementation &#8212; to move from code upward to intent, from mechanism upward to policy. The resulting artifact is not another program. It is a <em>description</em> of what the program does, expressed in terms that are independent of any particular language or machine.</p><p>I have watched LLMs read code and summarize it. That is the first step. But a summary is not a factbase. A factbase is structured. It can be queried. It can be reasoned over. It is what you need if you want to <em>rewrite</em> the logic, not just <em>describe</em> it.</p><p>The pipeline I have in mind is:</p><p>old code &#8594; LLM (Design Recovery) &#8594; factbase(s) &#8594; LLM (code generation) &#8594; new code</p><p>The first LLM pass reads the old code and externalizes the business rules into a relational form. Prolog is a natural target for this. Barliman is interesting. Any system that lets you assert facts and query their consequences will do. The factbase is the intermediate representation. It is language-agnostic. It captures <em>what</em> the system does without committing to <em>how</em>.</p><p>The second LLM pass reads the factbase and generates new code in the target language or languages. At that point the portability problem is solved &#8212; not by running the old code, but by reconstituting its logic in a new form.</p><p>The key insight is that business rules are the scarce resource. Code is not scarce. Code is reproducible. A senior developer can write a sorting routine in any language you name. What cannot be reproduced from thin air is the accumulated domain knowledge that explains <em>which</em> things to sort, <em>when</em>, and <em>what to do with the results</em>. That knowledge is what is lost when old code becomes unrunnable. That knowledge is exactly what Design Recovery is designed to extract.</p><p>The question is whether LLMs are good enough at the extraction step. My reading of current LLM capabilities says: almost, but not yet reliably. LLMs can tell you what a codebase contains. They are less reliable at telling you what a codebase <em>implies</em> &#8212; the dependencies between decisions, the invariants that are never stated but always assumed, the edge cases handled by code that looks like error handling but is actually domain logic.</p><p>This is where relational representation becomes useful beyond just storage. If the factbase is Prolog-like, you can run queries against it. You can ask whether the extracted rules are consistent. You can ask whether a proposed rewrite satisfies all the original constraints. Meta-programming over the factbase becomes a verification tool. The LLM extracts; the relational engine checks.</p><p>Barliman is particularly interesting in this role because it works bidirectionally &#8212; given constraints on a program&#8217;s behavior, it can synthesize a program that satisfies them. If the factbase encodes the behavior constraints recovered from the old code, Barliman is a natural candidate for the synthesis step.</p><p>The remaining engineering problem is granularity. LLMs summarize at the wrong granularity for reliable rewriting. A summary that is accurate enough to describe the system to a human is not detailed enough to regenerate the system for a machine. The extraction step needs to be pushed further &#8212; toward a factbase granular enough that a second LLM, reading only the factbase, can reproduce the observable behavior of the original code without having seen the original code at all.</p><p>That is the hard part. It is also the interesting part.</p><p><em>The pipeline above is not science fiction. All the components exist. The LLM side improves monthly. The Design Recovery and relational reasoning sides have decades of theory behind them. What is missing is a toolchain that connects them. That toolchain is worth building.</em></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p><em>Paid subscriptions are a voluntary way to support this work.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/llms-as-design-recovery-engines/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/llms-as-design-recovery-engines/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/llms-as-design-recovery-engines?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/llms-as-design-recovery-engines?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Jail-Breaking Parser Technology (A Slight Return)]]></title><description><![CDATA[2026-04-12]]></description><link>https://programmingsimplicity.substack.com/p/jail-breaking-parser-technology-a</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/jail-breaking-parser-technology-a</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Mon, 13 Apr 2026 02:40:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My view on parsers:</p><ol><li><p>parsers are just string pattern matchers, i.e. the &#8220;easiest&#8221; parts of compilers</p></li><li><p>&#8220;regular expressions&#8221; used to be a compiler-only technology (e.g. LEX), then made the leap into other uses, appearing in places like UNIX tools (grep, sed, etc.), perl, javascript, python, etc. It is time for &#8220;parsing&#8221; to make that leap, too, but, 50 years after the release of YACC, this hasn&#8217;t really happened yet.</p></li></ol><p>IMO, laborious CFG techniques discourage the thought processes for making this kind of leap whereas PEG techniques enable this kind of leap.</p><p>Making the leap into non-traditional compiler realms opens new doors and new views on programming techniques and even opens new doors on what we currently call &#8220;programming languages&#8221;.</p><p>IMO, current &#8220;programming languages&#8221; (GPLs - General Purpose Languages) are just variants of assembler. If we could simply stretch little syntactic skins over these assembler variants we could build new languages without needing to build more compilers from scratch, i.e. language design for the 21st century (IMO: SPL (Special Purpose Languages) not GPL, not-necessarily-Turing-complete, etc.) [<em>ask me for my examples of little languages stretched over existing languages, if interested</em>]</p><p><a href="https://programmingsimplicity.substack.com/p/jail-breaking-parser-technology?r=1egdky">previous article</a></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p><em>Paid subscriptions are a voluntary way to support this work.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/jail-breaking-parser-technology-a/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/jail-breaking-parser-technology-a/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/jail-breaking-parser-technology-a?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/jail-breaking-parser-technology-a?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Flow]]></title><description><![CDATA[2026-04-12]]></description><link>https://programmingsimplicity.substack.com/p/flow</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/flow</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Mon, 13 Apr 2026 02:06:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>Switching between thought streams wastes brain power. It is the cognitive equivalent of OS thrashing. Keeping your mind locked on one problem &#8212; &#8220;flow&#8221; &#8212; is a requirement for deep thought, for continuity, and for self-consistency of design. Breaking flow wastes time in thrashing, or, worse, makes you lose the thread of where the thought was leading.</p><p><strong>L;R</strong></p><p><strong>The Core Dump Era</strong></p><p>When programmers debugged in assembler, they examined core dumps.</p><p>A core dump is a raw snapshot of memory at the moment a program crashed. Finding the bug meant reading hexadecimal numbers and mapping them, manually, to program state. This was not just slow &#8212; it usually meant a full interruption. You had to hold the entire machine state in your head, reconstruct what the program was trying to do, and reason backwards to the fault.</p><p>Concentration was not optional. A single distraction usually meant starting over.</p><p><strong>What IDEs Actually Did</strong></p><p>We invented debuggers and IDEs. The common narrative is that they let us inspect program state more easily. That is true but incomplete.</p><p>What they really did was maintain flow.</p><p>Before a debugger, the feedback loop was: write code &#8594; compile &#8594; run &#8594; crash &#8594; decode dump &#8594; hypothesize &#8594; repeat. The core dump step was an enormous cognitive break. You left the problem domain entirely and entered a forensic domain. It was not that you entered a different mode of thought &#8212; it was that you lost continuity of the main mode. The thread you were holding snapped.</p><p>A debugger collapsed that loop. You stayed in the problem. You could watch values change in real time, set breakpoints near your hypothesis, confirm or refute quickly. The interruption was eliminated, or at least drastically reduced.</p><p>This is why IDEs let us build bigger programs. Not because they made any individual operation faster. Because they stopped the thrashing.</p><p><strong>Thrashing</strong></p><p>Operating systems thrash when they spend more time swapping pages than executing instructions. The work-to-overhead ratio inverts. The machine gets busier and busier while accomplishing less and less.</p><p>The brain does the same thing.</p><p>Every time you switch thought streams &#8212; from the problem you are solving to a niggly detail that interrupted you, and then back &#8212; you pay a re-entry cost. Reloading context. Reconstructing where you were. Remembering which threads matter and which do not. For complex problems, this cost is not seconds. It is minutes. And sometimes the thread is simply gone.</p><p>If the interruptions are frequent enough, you never get back to full depth. You skim the surface of each thought stream and switch again before you have accomplished anything meaningful.</p><p><strong>Flow Is Not Mystical</strong></p><p>Flow gets discussed as if it were a psychological luxury &#8212; something athletes and artists chase but engineers should feel slightly embarrassed about wanting.</p><p>That framing is wrong.</p><p>Flow is the state in which you are holding the problem, and only the problem, in working memory. It is a necessary condition for solving hard problems. It is not a bonus.</p><p>Gallwey named it in the context of sport, Csikszentmihalyi studied it formally. What all domains have in common is that the problems are complex enough that partial attention produces poor results. A basketball player in flow is not thinking about their feet. A programmer in flow is not thinking about syntax. They have both pushed the mechanics below conscious attention and freed their processing for the actual problem.</p><p>The mechanics have to be automated enough &#8212; or supported well enough by tooling &#8212; that they do not demand attention at all.</p><p>Flow is hard to measure. That is precisely why it tends not to be taught in school and tends not to appear in design requirements. But we need to be very cognizant of any feature, workflow, or tool choice that affects it.</p><p><strong>What Breaks Flow</strong></p><p>The obvious ones first. Notifications interrupt flow. Menus interrupt flow &#8212; even simple ones, because choosing requires shifting attention from the problem to the interface. Modal dialogs interrupt flow. Slow builds interrupt flow because they create a gap long enough that you wonder whether to do something else, and sometimes you do.</p><p>Saving a file is a small example worth noticing. Apple Notes and Obsidian save documents transparently, without any user action required. Emacs requires you to remember to save. That is an old-fashioned requirement from the 20th century &#8212; a demand that you appease the editor before you can trust your own work exists. Every &#8220;did I save?&#8221; moment is a micro-interruption, a small withdrawal from the concentration budget.</p><p>Two larger ones deserve their own treatment.</p><p><strong>Strong typing.</strong> Type checkers, even after fifty years of development, are not perfect. They require you to stop and appease the type checker. When the type checker disagrees with you, you are snapped onto a different thought path &#8212; one dedicated not to the actual problem, but to satisfying a tool&#8217;s static analysis. This is not an argument against type checking. It is an argument for being honest about what it costs.</p><p><strong>Changing your mind.</strong> As a project evolves, you learn new aspects of it &#8212; new nuances, new constraints, new understanding of what the design should be. Changing your mind about the design is easy. Going back and changing existing code, existing tests, existing assumptions, is an interruption. In compiler construction, for instance, if you change your mind about what the parser needs to do, you may need to divert your thoughts to whether the scanner needs to change too. Every dependent piece pulls you away from the idea that prompted the change in the first place.</p><p>The Waterfall method, ironically, was an attempt to preserve flow on a single vector &#8212; decide everything up front and never revisit it. That preserves a certain kind of continuity but breaks the ability to learn from the work as it develops. What we actually need are tools that make design changes instantaneous &#8212; so you can update your thinking without stopping to manage the consequences across a codebase.</p><p><strong>Notation Is a Flow Tool</strong></p><p>A notation that matches the problem domain keeps you in the problem. A notation that requires translation &#8212; from what you are thinking to what the language can express &#8212; is a context switch tax on every line.</p><p>This is why domain-specific notations matter. Not because general-purpose languages are wrong, but because a notation that does not match your problem forces you to think in two domains simultaneously. That is not flow. That is thrashing.</p><p>The deeper point is that no single language can satisfy flow concerns across the full range of problems, because every non-trivial problem involves multiple paradigms. What is actually needed is a workflow &#8212; or an IDE, or a composition tool &#8212; that allows the programmer to work with multiple notations simultaneously: little languages, sketches, diagrams, each appropriate to the aspect of the problem it addresses. The unit of composition is not the language. It is the thought.</p><p><strong>The Sports Analogy Is Not a Metaphor</strong></p><p>In competitive sports, staying in the zone is a performance variable coaches actively manage. Pre-game rituals, routine warm-ups, elimination of distractions &#8212; all of it exists to reach and sustain flow before and during performance.</p><p>Programming is not different. The stakes are just less visible because bugs do not score points for the other team.</p><p>If we took flow as seriously in programming as coaches take it in sports &#8212; if we designed tools, workspaces, and workflows around preserving concentration &#8212; we would build better software. Not incrementally better. Significantly better.</p><p><strong>The Practical Consequence</strong></p><p>Every tool decision is a flow decision.</p><p>Does this tool require me to understand it before I can understand my problem? Does this abstraction require me to think at two levels simultaneously? Does this workflow interrupt me at the moments I am going deepest?</p><p>These are not comfort questions. They are engineering questions. The answer affects throughput, defect rate, and the size and complexity of problems we can attempt at all.</p><p>The core dump was not just slow. It was a flow killer. The IDE was not just convenient. It was a flow restorer. That is why it changed everything.</p><p><strong>Flow Is a Design Requirement</strong></p><p>We treat flow as a side effect &#8212; something that happens when everything else goes right.</p><p>It should be a first-class requirement. Listed alongside correctness, performance, and maintainability. Evaluated deliberately.</p><p>When designing a new tool, the question is not only &#8220;does it work?&#8221; The question is also: does it keep the programmer in the problem, or does it pull them out of it?</p><p><strong>Languages.</strong> A language that requires you to appease the compiler before you can express your idea is a flow tax. Boilerplate, type annotation ceremonies, incantations that satisfy the toolchain but communicate nothing about the problem &#8212; these are mandatory context switches, paid on every line. But further: a single language cannot satisfy flow across the full problem space. The shape of the solution should determine the notation, not the other way around. Flow-preserving tooling makes it possible to compose multiple notations &#8212; each a good fit for its subdomain &#8212; rather than forcing every thought into a single syntax.</p><p><strong>Compilers.</strong> Error messages that describe the compiler&#8217;s internal confusion instead of the programmer&#8217;s likely mistake force a translation step. You decode what the compiler means and map it back to what you did. Python is a clear example &#8212; a single exception fills the screen with backtrace details, making you wade through the tool&#8217;s internal machinery to find what went wrong in your problem. Every decoding step is a micro-interruption. A compiler designed for flow gives you the error in terms of your problem, not its implementation.</p><p><strong>IDEs.</strong> A &#8220;programming language&#8221; is, in one light, a simple caveman IDE &#8212; an interface for expressing intent as machine code. GPLs are one variant of that interface, not the only one. The original IDE insight was correct: collapse the feedback loop, stay in the problem. But IDEs have since accumulated their own surface area &#8212; plugin ecosystems, configuration panels, preference trees. An IDE that requires you to understand the IDE is partially defeating its own purpose. An IDE designed for flow disappears. And in the 21st century, where problem spaces span paradigms that no single language covers, the IDE needs to go further: it needs to let you compose multiple notations without leaving the environment or breaking your thought.</p><p><strong>Debuggers.</strong> Many early languages worked in a REPL-like manner &#8212; the language and the debugger were the same thing, expressed in the same notation. You built the system and interrogated it using identical syntax. You never left the language. That unity is what made it a flow tool: there was no context switch between &#8220;building&#8221; and &#8220;inspecting.&#8221; That unity was abandoned in favour of the edit-compile-run cycle, and the debugger became a separate instrument with its own interface to learn and operate.</p><p>Modern programmers use the word REPL but the point has been largely lost. The original insight was not &#8220;evaluate expressions interactively.&#8221; It was &#8220;never make the programmer change mental gears to examine what they just built.&#8221;</p><p>A modern version of this concept goes further, because the problem has grown. Traditional REPL-based languages enforced a single notation &#8212; which meant the programmer stayed in flow by staying in one syntax, but was also trapped in one paradigm. Every problem had to be expressed through that paradigm&#8217;s eye of the needle, whether it fit or not. State-machine control flow expressed in a functional language. Asynchronous concurrency forced into synchronous sequential notation. Each mismatch is a flow tax: you spend time and attention translating what you actually mean into what the language can accept.</p><p>A REPL designed for flow in the 21st century would let the programmer stay in the problem across paradigm boundaries &#8212; composing multiple notations, each a natural fit for its subdomain, interrogating any of them using the same environment. The goal is the same as the original REPL: no gear change between thinking and building. The difference is that modern problem spaces are too large and too varied to be squeezed into any single paradigm without breakage.</p><p>The key structural move here is: first honour what the original REPL got right (same-notation unity = flow), then name what it got wrong (single paradigm = hidden flow tax), then project forward to what the modern version would actually need to be.</p><p><strong>Workflows.</strong> Slow build pipelines, review processes that fragment deep work into hour-long slots, notification systems that fire on someone else&#8217;s schedule &#8212; these are organisational flow killers. A workflow designed for flow protects concentration as a resource. Slow builds get fixed. Interrupts get batched. Deep work gets time that is actually unbroken.</p><p>The principle is simple. Every mandatory context switch costs concentration. Every tool that demands attention for itself steals attention from the problem. The goal of tool design is to reduce that theft to zero.</p><p>We do not build tools so programmers can use tools. We build tools so programmers can think.</p><p>Flow is what thinking looks like when nothing is getting in the way.</p><p><strong>Further Reading</strong></p><p><strong>Alan Kay, &#8220;Doing With Images Makes Symbols.&#8221;</strong> At the 57:42 mark, Kay shows a video of Timothy Gallwey teaching a woman named Molly &#8212; who has never played tennis &#8212; to play competently in twenty minutes, by keeping her analytical mind out of the way and letting her body learn. The point is about flow in learning, and it applies directly to tool design: a tool that forces the user to think about the tool prevents the user from entering the state where real learning and real work happen. <a href="https://youtu.be/p2LZLYcu_JY?t=3462">Watch from that moment here.</a></p><p><strong>W. Timothy Gallwey, </strong><em><strong>The Inner Game of Tennis</strong></em><strong> (1974).</strong> The book behind the Molly episode. Gallwey&#8217;s framework &#8212; that performance is blocked by the internal interference of the analytical mind &#8212; maps directly onto the programmer&#8217;s experience of being pulled out of deep work by tooling concerns. The inner game is the one you play against your own distraction. The outer game is the code.</p><p><strong>Jef Raskin, </strong><em><strong>The Humane Interface</strong></em><strong> (2000).</strong> Raskin attempts to describe, in explicit engineering terms, the aspects of computer interface design that interrupt concentration &#8212; and those that preserve it. Where most interface design literature stays qualitative, Raskin tries to quantify the cost of mode switches, attention captures, and interface elements that demand cognitive engagement at the wrong moment. Directly relevant to anyone designing tools with flow in mind.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p></p><p><em>Paid subscriptions are a voluntary way to support this work.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/flow/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/flow/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/flow?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/flow?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Structured Software Architecture]]></title><description><![CDATA[Eliminating Namespace Issues using Nested and Layered Part Composition 2026-04-09]]></description><link>https://programmingsimplicity.substack.com/p/structured-software-architecture</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/structured-software-architecture</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Thu, 09 Apr 2026 13:46:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/36838aba-c1c8-4611-b9b3-dc8bf2e28712_501x191.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nO1a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nO1a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 424w, https://substackcdn.com/image/fetch/$s_!nO1a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 848w, https://substackcdn.com/image/fetch/$s_!nO1a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 1272w, https://substackcdn.com/image/fetch/$s_!nO1a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nO1a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png" width="501" height="191" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab741565-b801-405f-82e1-aa08fa67f233_501x191.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:191,&quot;width&quot;:501,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27797,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/193689070?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nO1a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 424w, https://substackcdn.com/image/fetch/$s_!nO1a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 848w, https://substackcdn.com/image/fetch/$s_!nO1a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 1272w, https://substackcdn.com/image/fetch/$s_!nO1a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab741565-b801-405f-82e1-aa08fa67f233_501x191.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>There are 3 Parts on this diagram. The Container part has kind (type) &#8220;P&#8221;. It contains 2 child parts, one of kind &#8220;A&#8221; and another of kind &#8220;B&#8221;. The Container &#8220;P&#8221;, also, contains one wire, drawn as an arrow between ports &#8220;x&#8221; and &#8220;y&#8221;.</p><p>Container Part &#8220;P&#8221; sees</p><ul><li><p>Part of kind A and its port A.x</p></li><li><p>Part of kind B and its port B.y</p></li><li><p>wire [&#8220;across&#8221;, A.x, B.y]</p></li></ul><p>Container &#8220;P&#8221; cannot see</p><ul><li><p>A.a, A.b, A.c, A.d, A.e, A.f (any of the entities inside of A)</p></li><li><p>B.s, B.t, B.u, B.v (any of the entities inside of B)</p></li></ul><p>Part A does not see part B, nor B.y, nor any of the named entities inside of B</p><p>Part B does not see Part A, nor A.X, nor any of the named entities inside of A</p><p>Parts P, A, B have one (and only one) input queue, each</p><p>Parts P, A, B have one (and only one) output queue, each</p><p><strong>Sending an Output Message (Mevent)</strong></p><p>1. A sends message &#8220;Hello&#8221; to its own port &#8220;x&#8221;. (N.B. to its own output port, not to B&#8217;s y port).</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rc7u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rc7u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 424w, https://substackcdn.com/image/fetch/$s_!rc7u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 848w, https://substackcdn.com/image/fetch/$s_!rc7u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 1272w, https://substackcdn.com/image/fetch/$s_!rc7u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rc7u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png" width="201" height="41" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/70350c12-50ba-4155-b440-7729bab8e36e_201x41.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:41,&quot;width&quot;:201,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15985,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/193689070?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rc7u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 424w, https://substackcdn.com/image/fetch/$s_!rc7u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 848w, https://substackcdn.com/image/fetch/$s_!rc7u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 1272w, https://substackcdn.com/image/fetch/$s_!rc7u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70350c12-50ba-4155-b440-7729bab8e36e_201x41.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>2. The Mevent send consists of mapping the output mevent of A into an input mevent for B. P performs this mapping then enqueues the new message onto B&#8217;s input queue</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6l1E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6l1E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 424w, https://substackcdn.com/image/fetch/$s_!6l1E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 848w, https://substackcdn.com/image/fetch/$s_!6l1E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 1272w, https://substackcdn.com/image/fetch/$s_!6l1E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6l1E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png" width="521" height="41" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/971009d5-f533-4b4f-b489-2340abf664e1_521x41.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:41,&quot;width&quot;:521,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21456,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/193689070?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6l1E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 424w, https://substackcdn.com/image/fetch/$s_!6l1E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 848w, https://substackcdn.com/image/fetch/$s_!6l1E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 1272w, https://substackcdn.com/image/fetch/$s_!6l1E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F971009d5-f533-4b4f-b489-2340abf664e1_521x41.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>Discussion</strong></p><p><em>Expressing the following rules in English makes them appear to be more complicated than necessary. The basic idea is essentially &#8220;do not colour outside of the lines&#8221; - an idea that is taught to children at an early age in pre-school.</em></p><p>Parts are essentially &#8220;black boxes&#8221;.</p><p>Parts are nested and layered and isolated from one another. Parts cannot reach in across the boundaries of other parts and access their internals. Wires (arrows) can only connect ports (<em>note the spelling, port not part</em>)<em> </em>to other ports. Wires cannot cross edges of parts (nor ports).</p><p>Wires (arrows) are entirely contained within container parts. Wires are owned by container parts, not children of the containers. A child cannot refer sideways to any other sibling part. A child cannot refer upwards to a parent part.</p><p>A child part can be either a Container part or a Leaf part. The parent cannot know how the child is implemented and whether the child is a Container or a Leaf. If a child is implemented as a Container, it can only refer to its own, direct children and its own wires.</p><p>This (recursive) nesting hides internal namespaces. A Container part can only see its children&#8217;s kinds (types) and the external-facing ports provided by each child [<em>akin to &#8220;pins&#8221; on hardware ICs</em>]. Since each part, whether Container or Leaf, is otherwise opaque, children&#8217;s names do not leak out of Container parts.</p><p>Namespacing issues are restricted in scope. Internal names do not leak out to enclosing parts. Each layer sees only a small number of names.</p><p>Parts, themselves, do not have explicit names (the &#8220;compiler&#8221; tracks each part individually using graphical coordinates).</p><p>There are no &#8220;name clashes&#8221; due to part kinds. A container can contain many parts of the same kind (P, A, B in the above example), since the underlying system differentiates parts by position instead of by name or kind.</p><p>There are no &#8220;name clashes&#8221; due to port names, since wires carry output to input mapping information. [<em>Actually, source to sink mapping - I choose to skip over this nuance here</em>].</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/structured-software-architecture/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/structured-software-architecture/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/structured-software-architecture?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/structured-software-architecture?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Little Language Case Study - Generating Code for Simple State Machines]]></title><description><![CDATA[Why draw a diagram when you could write 50+ lines of boilerplate instead? 2026-04-08]]></description><link>https://programmingsimplicity.substack.com/p/little-language-case-study-generating</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/little-language-case-study-generating</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Wed, 08 Apr 2026 12:55:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!i9rd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There&#8217;s a moment every programmer knows: you&#8217;re staring at a blob of nested if statements and callback hell, and you think &#8212; <em>I know exactly what this is supposed to do, I just can&#8217;t see it anymore.</em></p><p>State machines are one of the classic cures. They&#8217;re one of computing&#8217;s oldest and cleanest ideas: a system exists in one of a finite number of <em>states</em>, and transitions between them are triggered by <em>events</em> or <em>conditions</em>. Compilers use them. Network protocols are built on them. Game AI runs on them. Traffic lights run on them.</p><p>And yet, when we go to <em>implement</em> a state machine in a general-purpose language, something unfortunate happens.</p><p><strong>The Diagram vs. The Code</strong></p><p>Consider a simple looping state machine &#8212; the kind you might use to track a value bouncing between two bounds. Here&#8217;s the design, expressed as a diagram:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i9rd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i9rd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 424w, https://substackcdn.com/image/fetch/$s_!i9rd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 848w, https://substackcdn.com/image/fetch/$s_!i9rd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 1272w, https://substackcdn.com/image/fetch/$s_!i9rd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i9rd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png" width="700" height="645" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6594c424-eb35-449a-b17d-845969a377de_700x645.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:645,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105006,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/193569151?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i9rd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 424w, https://substackcdn.com/image/fetch/$s_!i9rd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 848w, https://substackcdn.com/image/fetch/$s_!i9rd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 1272w, https://substackcdn.com/image/fetch/$s_!i9rd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6594c424-eb35-449a-b17d-845969a377de_700x645.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>(three states &#8212; <strong>idle</strong>, <strong>wait for w recrossing</strong>, <strong>wait for zero recrossing</strong> &#8212; with guarded transitions between them)</em><br><br>The logic is immediately legible:</p><ul><li><p>From <strong>idle</strong>: if x &gt; w, call reverse() and move to <em>wait for w recrossing</em>. If x &lt; 0, call reverse() and move to <em>wait for zero recrossing</em>.</p></li><li><p>From <strong>wait for w recrossing</strong>: if x &lt; w, return to <em>idle</em>.</p></li><li><p>From <strong>wait for zero recrossing</strong>: if x &gt; 0, return to <em>idle</em>.</p></li></ul><p>That&#8217;s the whole machine. You could explain it to a non-programmer in two minutes.</p><p><em>A practical note: the code annotating each transition (the guards and actions) is written in raw Python. The state machine runtime expects an object &#8212; called e &#8212; through which the surrounding code exposes variables and functions like x, w, and reverse(). This is a deliberate separation: the diagram owns the control flow, and the host code owns the domain logic.</em></p><p>Now look at the Python that implements it &#8212; over 50 lines generated from that exact diagram:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Bl8_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bl8_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 424w, https://substackcdn.com/image/fetch/$s_!Bl8_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 848w, https://substackcdn.com/image/fetch/$s_!Bl8_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 1272w, https://substackcdn.com/image/fetch/$s_!Bl8_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bl8_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png" width="784" height="980" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:980,&quot;width&quot;:784,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:240111,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/193569151?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Bl8_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 424w, https://substackcdn.com/image/fetch/$s_!Bl8_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 848w, https://substackcdn.com/image/fetch/$s_!Bl8_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 1272w, https://substackcdn.com/image/fetch/$s_!Bl8_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb5f5dde-ff77-46c4-9123-2c7b5cb6d8a3_784x980.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It&#8217;s correct. It works. And it&#8217;s a completely opaque rendering of something that was <em>obvious</em> in the diagram while the Python code buries the intended control flow under a layer of low-level detail, forcing a functional representation onto something that is fundamentally a control flow structure.</p><p>The control flow architecture has been obscured. The states are still there if you squint, but the <em>shape</em> of the machine &#8212; the gestalt that makes state machines so powerful for reasoning &#8212; has been smeared across a class definition.</p><p><strong>The Pattern Hidden in the Boilerplate</strong></p><p>The generated code reveals a consistent pattern. Every state is represented by exactly <strong>three functions</strong>:</p><ul><li><p><code>enter_&lt;state&gt;</code> &#8212; called once when entering the state</p></li><li><p><code>step_&lt;state&gt;</code> &#8212; called repeatedly; evaluates guards and fires transitions</p></li><li><p><code>exit_&lt;state&gt;</code> &#8212; called once when leaving the state</p></li></ul><p>A transition from one state to the next follows a fixed multi-step ritual inside the current state&#8217;s step function:</p><ol><li><p>Evaluate the guard (e.g. if x &gt; w)</p></li><li><p>If true: call exit from the current state</p></li><li><p>Call any transition action (e.g. reverse())</p></li><li><p>Call enter into the next state</p></li></ol><p>The machine itself is a class with a state variable and a dispatch table that routes step() calls to the right function for the current state.</p><p>This is all perfectly mechanical. There&#8217;s no creativity in writing it. It&#8217;s transcription work &#8212; and that&#8217;s exactly what code generation is for.</p><p><strong>Little Languages, Big Leverage</strong></p><p>The diagram isn&#8217;t just documentation. It <em>is</em> the program &#8212; expressed in what I call a <strong>DPL</strong> (Diagrammatic Programming Language).</p><p>DPLs are &#8220;little languages.&#8221; They make no pretense of Turing completeness. They don&#8217;t try to solve every possible problem. They solve <em>one</em> problem &#8212; in this case, expressing state-machine control flow &#8212; and they solve it well.</p><p>The key insight is that a DPL doesn&#8217;t replace your general-purpose language. It <em>generates snippets</em> for it. The Python code above is intended to be embedded inside a larger project, like a component you&#8217;d pull off a shelf. The diagram specifies the control flow skeleton; the surrounding Python code supplies the domain logic (reverse(), the bounds checking, whatever the machine needs to operate).</p><p>This is a genuinely useful division of labour:</p><ul><li><p><strong>The diagram</strong> expresses <em>what</em> states exist and <em>when</em> to transition between them.</p></li><li><p><strong>The surrounding code</strong> expresses <em>what to do</em> on entry, exit, and transition.</p></li></ul><p>Neither half needs to know too much about the other.</p><p><strong>Treating Python Like Assembly</strong></p><p>Here&#8217;s the provocative framing: once you have a code generator, your general-purpose programming language becomes an <em>assembly language</em> for your project.</p><p>This isn&#8217;t an insult to Python. Assembly languages are precise, powerful, and absolutely essential. But nobody argues that you should write your entire project in assembly &#8212; you want to work at a higher level of abstraction, and <em>compile down</em> to assembly for execution.</p><p>The same logic applies here. When your project has components that are naturally expressed as state machines (and many do), you should be able to <em>draw</em> those components and compile down to Python &#8212; not transcribe them by hand.</p><p>The code generator at <a href="https://github.com/guitarvydas/sm">github.com/guitarvydas/sm</a> does exactly this. Drop a .drawio diagram of your state machine into the repo and run @make. Out comes the boilerplate Python. You supply the guards and actions; the tool supplies the structure.</p><p>[<em>See README.md for the exact recipe.</em>]</p><p><strong>The Workbench We Should Want</strong></p><p>There&#8217;s a bigger argument lurking here.</p><p>We&#8217;ve spent decades building programming languages that try to do <em>everything</em> &#8212; general-purpose, Turing-complete, expressive, fast, safe, concurrent. And those languages are remarkable achievements.</p><p>But the ambition to make one language handle every problem has a shadow side: it makes us reach for that one language even when a smaller, more targeted tool would serve us better. We hand-write state machine boilerplate when we could draw diagrams. We build elaborate data transformation pipelines in imperative code when a declarative notation would be cleaner. We implement parsers by hand when a grammar DSL would be more readable and more maintainable.</p><p>The alternative is a <strong>programming workbench</strong>: a composition of tools, each doing one thing well, connected by code generation and interoperability. You draw where drawing is clearest. You write where writing is clearest. The workbench stitches it together.</p><p>That&#8217;s not a new idea &#8212; it&#8217;s the Unix philosophy applied to the act of programming itself. Small tools. Sharp edges. Composable outputs.</p><p>The state machine generator is a small demonstration of what that workbench could feel like. The diagram is unambiguous. The generated code is mechanical. The human writes only the interesting parts.</p><p><strong>Try It</strong></p><p>The tool is at <a href="https://github.com/guitarvydas/sm">github.com/guitarvydas/sm</a>. It&#8217;s lightly tested &#8212; consider this early-stage and experimental &#8212; but the concept is solid and the generated code for a diagram like the one above works as expected.</p><p>If you&#8217;re reaching for nested conditionals to track some mode in your program, draw the states first. You might find the diagram says everything you need &#8212; and that the code writes itself.</p><p><em>What would your programming workflow look like if you could compose it from little languages, each suited to its part of the problem? I&#8217;d love to hear what you&#8217;d reach for first.</em></p><p><strong>Further: Ideas and Observations</strong></p><p>Ideas and observations about other little languages might be of interest:</p><p><a href="https://programmingsimplicity.substack.com/p/what-ive-learned-from-forth-haiku?r=1egdky">What I&#8217;ve Learned from Forth Haiku Thus Far</a> and other related Forth Haiku articles on my substack.</p><p><a href="https://www.youtube.com/playlist?list=PLHh2_dCKBPjaA3z6dC8tntQE3ypp_7Wvm">Forth Haiku playlist on YouTube</a></p><p><a href="https://programmingsimplicity.substack.com/p/is-sector-lisp-small-only-due-to?r=1egdky">Sector Lisp</a></p><p><a href="https://www.youtube.com/playlist?list=PLHh2_dCKBPjYEGrLE2IJAXp5V8eHBwzu4">SCN playlist</a></p><p><a href="https://www.youtube.com/playlist?list=PLHh2_dCKBPjYhpvWSvJNJdrsZE8lNHza7">Decision Tree DPL playlist</a></p><p><a href="https://www.youtube.com/playlist?list=PLHh2_dCKBPjY_X1Pp0Rj--qhQLg8YuMNs">Ohm-JS playlist</a></p><p><a href="https://programmingsimplicity.substack.com/p/experiments-with-text-to-text-transpilation?r=1egdky">Experiments with Text to Text Transpilation</a> - along with articles on my substack containing the keyword &#8216;T2T&#8217;</p><p><a href="https://programmingsimplicity.substack.com/p/pbp-part-basics?r=1egdky">PBP Part Basics</a> - along with articles containing the keywords &#8216;PBP&#8217;, &#8216;Parts Based Programming&#8217;, &#8216;0D&#8217;</p><p><a href="https://www.youtube.com/playlist?list=PLHh2_dCKBPjbBN2R8xwBiS4nHlo5iQjqS">PBP Cookbook playlist</a></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/little-language-case-study-generating/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/little-language-case-study-generating/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/little-language-case-study-generating?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/little-language-case-study-generating?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Case for Composable Notations (6 of 5) - WIP - Notations in Progress]]></title><description><![CDATA[2026-03-30]]></description><link>https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-0d3</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-0d3</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 31 Mar 2026 02:25:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The series ends here. The work doesn&#8217;t.</p><p>What follows is a brief appendix describing notation experiments currently in progress. Collaborators and kibitzers are welcome. More discussion of these ideas happens on the <strong><a href="https://discord.gg/6B5AmNuVhC">Programming Simplicity</a></strong> Discord server. The repos contain WIP code &#8220;as is&#8221;.</p><p><strong>dtree</strong></p><p>dtree is a DaS notation &#8212; Diagrams as Syntax. It uses three primitives: rhombus (decision), yes/no arrows, and process boxes. Turing completeness is not a goal. The notation is useful precisely because it is narrow &#8212; easy to imagine, easy to build, easy to read. The simplicity is the feature.</p><p><em>Repo: [<a href="https://github.com/guitarvydas/dtree">https://github.com/guitarvydas/dtree</a>]</em></p><p><strong>sm</strong></p><p>sm is also DaS. It expresses state machines directly as diagrams. A state in sm is not a case statement or a single closure &#8212; each state requires three functions, which makes it cumbersome to express in FP notation and therefore routinely side-stepped. sm makes it natural.</p><p><em>Repo: [<a href="https://github.com/guitarvydas/sm">https://github.com/guitarvydas/sm</a>]</em></p><p><strong>Frish</strong></p><p>Frish &#8212; FoRth-ISH &#8212; is TaS (Text as Syntax). The Frish language is text-based; its compiler is DaS. Forth&#8217;s core idea &#8212; extremely small, composable operations &#8212; without Forth&#8217;s historical baggage.</p><p><em>Repos: </em></p><p><em>[<a href="https://github.com/guitarvydas/frish/tree/main">https://github.com/guitarvydas/frish/tree/main</a>]</em><br><em>[<a href="https://github.com/guitarvydas/frish/tree/debugdefsynonym">https://github.com/guitarvydas/frish/tree/debugdefsynonym</a>]</em></p><p><strong>0d.rt</strong></p><p>0d.rt is TaS. It implements UNIX-like processes in Python using message passing via Python&#8217;s dequeue class. It currently emits Common Lisp and JavaScript in addition to Python &#8212; other targets are possible but I ran out of road.</p><p>A few design decisions worth noting:</p><p>The original implementation used various complicated data structures. It was eventually upgraded to use only strings, parsed on demand using OhmJS/PEG. This seems wildly inefficient by 20th-century standards. It probably isn&#8217;t &#8212; most programming languages are, at their core, parsers for data expressed as text, preened ahead of time. JIT compilation is likely the right model here, not pre-declared structure.</p><p>The deeper principle: systems must not be built around fixed, centralised data structure definitions &#8212; no central authority should dictate communication protocols. Parts must be free to negotiate, on the fly, what they can and cannot accept. Strings, with known formats, make this tractable. Complex shared .h-style headers make it brittle.</p><p><em>Repo: [<a href="https://github.com/guitarvydas/pbp-dev/blob/main/kernel/0d.rt">https://github.com/guitarvydas/pbp-dev/blob/main/kernel/0d.rt</a>]</em></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-0d3/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-0d3/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-0d3?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-0d3?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Case for Composable Notations (5 of 5) UNIX Already Showed Us the Way]]></title><description><![CDATA[2026-03-30]]></description><link>https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-f31</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-f31</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 31 Mar 2026 02:21:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>UNIX demonstrated that programs composed from small, single-purpose tools connected by pipelines are more flexible than monolithic programs that try to do everything. The same principle applies to programming notations. We don&#8217;t need to start over. We need to start composing.</p><p><strong>L;R</strong></p><p>The previous articles in this series have been largely critical. FP imposes restrictions. State has been hidden rather than expressed. Ease of expression has been sacrificed for notational monoculture. The natural question is: what do we do instead?</p><p>The answer is not to push the big red button and start over. The answer is already partially in front of us. UNIX demonstrated it fifty years ago. We just haven&#8217;t finished reading the lesson.</p><p><strong>What UNIX actually showed us.</strong></p><p>The UNIX philosophy was built around a small number of powerful ideas. Programs should do one thing well. Programs should produce output that can become the input of another program. Programs should be composable.</p><p>The mechanism was text. UNIX chose plain text as the universal interface between programs. Not because text is the best possible representation for every kind of data &#8212; it isn&#8217;t &#8212; but because text is the one representation that every tool can read and write without negotiation. Text is the lowest common denominator that makes composition possible.</p><p>The result was pipelines. A sequence of small programs, each transforming text in one specific way, connected so that the output of one becomes the input of the next. No single program in the pipeline needs to know what the others do. Each program sees text in, produces text out, and minds its own business.</p><p>This is not just a technique for manipulating documents. It is a model for composing notations.</p><p><strong>Source code is text.</strong></p><p>Source code is text. This is obvious but underexploited. If source code is text, then the UNIX pipeline model applies directly: a program that reads source code in one notation and produces source code in another notation is a text-to-text transmogrifier &#8212; a t2t tool. Chain enough of them together and you have a pipeline that accepts source in one notation and delivers executable behaviour, passing through whatever intermediate notations are useful along the way.</p><p>This is not a new idea. It is how most compilers work internally &#8212; a sequence of transformations from source text through intermediate representations to machine code. The insight is to make the intermediate stages explicit, accessible, and composable at the level of the programming workflow rather than buried inside a single tool.</p><p>When you do that, each stage in the pipeline can use a different notation. A diagram tool produces a structured description of component topology. A t2t transmogrifier converts that description into a skeleton in a general-purpose language. Another transmogrifier fills in behaviour from a domain-specific notation. Another checks the result against constraints expressed in a relational notation. The final stage produces code in whatever target language the runtime expects.</p><p>No single notation does everything. Each notation does one thing, in its sweet spot, and hands off to the next.</p><p><strong>Little languages over GPLs.</strong></p><p>General-purpose programming languages are an answer to the question: what if we had to pick one notation for everything? The answer is inevitably a compromise. A GPL must be expressive enough to handle every problem domain, which means it cannot be optimally expressive for any particular one.</p><p>Little languages &#8212; small, purpose-built notations for specific problem domains &#8212; make the opposite trade. A little language for describing component connections does not need to handle string manipulation or arithmetic. A little language for expressing state transitions does not need to handle network I/O. Each little language is narrow, and narrow is what makes it clear.</p><p>BNF is a little language for describing grammars. It has been enormously productive precisely because it is narrow &#8212; it does one thing and does it with a clarity that no GPL-based alternative matches. The programmer says <em>what</em> the grammar is, not <em>how</em> to parse it. The notation carries the intent directly.</p><p>The objection is usually: little languages are expensive to build. This was true when language tools were scarce and difficult to use. It is much less true now. Parser generators, PEG libraries, macro systems, and pattern-matching tools have made the construction of small, purpose-built notations a realistic part of a programming workflow rather than a research project. The cost argument has not kept pace with the tooling.</p><p><strong>Existing languages as assembly languages.</strong></p><p>We do not need to abandon the languages we have. We need to change their role.</p><p>A general-purpose language &#8212; Python, JavaScript, Rust, whatever the current choice is &#8212; can serve as an assembly language for a higher-level notation. The higher-level notation expresses the problem in its natural terms. The t2t transmogrifier converts that expression into the GPL. The GPL handles the runtime details. The programmer works at the higher level and does not need to think about the GPL except when debugging the transmogrifier.</p><p>This is not theoretical. It is how most templating systems work. It is how CSS preprocessors work. It is how build systems with declarative configuration work. The pattern is familiar. The step that remains is to generalise it &#8212; to treat the composition of notations as a first-class part of programming practice rather than a specialised technique used in specific tool-building contexts.</p><p><strong>Composing paradigms.</strong></p><p>The functional paradigm has a sweet spot. The relational paradigm has a sweet spot. State machine notations have a sweet spot. Diagrammatic notations have a sweet spot. None of these sweet spots covers the full range of problems that real software systems contain.</p><p>A system composed from multiple notations, each handling the part of the problem it fits best, connected by t2t transmogrifiers into a coherent pipeline, is not more complex than a system forced into a single notation. It is less complex &#8212; because the notation at each stage is close to the problem at that stage, and proximity between notation and problem is where complexity goes to die.</p><p>The composition layer &#8212; the pipeline &#8212; does not need to be sophisticated. UNIX demonstrated that plain text and simple conventions are sufficient. The sophistication lives in the individual tools, where it belongs, not in the connection mechanism.</p><p><strong>PBP as a current experiment.</strong></p><p>Parts Based Programming &#8212; PBP &#8212; is an ongoing experiment in this direction. The core idea is that software components should be isolated, asynchronous by default, and connected by explicit routing rather than direct function calls &#8212; modelled on how hardware components behave rather than how mathematical functions compose.</p><p>The toolchain is a pipeline of t2t transmogrifiers. Diagrams produced in draw.io describe component topology. A transmogrifier converts those diagrams to a structured representation. Further transmogrifiers convert that representation into runnable code. Each stage uses the notation appropriate to its task.</p><p>The experiment is not finished. It is not a product. It is a demonstration that the pipeline model works for composing notations in a software development workflow, using tools that exist today, without requiring a new runtime or a new theory of computation.</p><p>The path forward does not require abandoning what we have. It requires treating what we have as a set of components &#8212; each with its own sweet spot, each expressible in a notation suited to it &#8212; and learning to compose those components as deliberately as UNIX learned to compose programs.</p><p>The pipes are already there. We just need to start using them.</p><p><em>End of series.</em></p><p><em>This is article 5 of 5 in &#8220;The Case for Composable Notations.&#8221; A Part 6 of 5 follows &#8212; an appendix describing notation experiments currently in progress, with pointers to repos and an open invitation to collaborate.</em></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-f31/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-f31/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-f31?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-f31?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Case for Composable Notations (4 of 5) Ease of Expression Is the Whole Point]]></title><description><![CDATA[2026-03-30]]></description><link>https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-2bd</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-2bd</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 31 Mar 2026 02:18:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>&#8220;You can express it in FP&#8221; is not good enough. If expressibility were sufficient, we&#8217;d still be programming in transistors. Notation shapes what gets built, what gets attempted, and what gets imagined.</p><p><strong>L;R</strong></p><p>There is a standard defence of any dominant notation when its limitations are raised: yes, but you <em>can</em> express that in it. You can express concurrency in a functional language. You can express state machines in a functional language. You can express asynchronous behaviour in a functional language. It takes more work, but it can be done.</p><p>This defence misses the point entirely.</p><p><strong>The transistor argument.</strong></p><p>We do not program in transistors. We could &#8212; every computation that runs on modern hardware is, at the lowest level, a pattern of transistors switching states. The transistor is expressive enough, in principle, to encode any program we have ever written or will ever write.</p><p>We do not program in transistors because the distance between transistors and human problems is too large. The notation does not fit the problem. Bridging that distance costs more than it is worth when better notations exist.</p><p>We do not describe programs in terms of molecules either, or atoms, or semiconductor physics. Each of those notations is, in principle, expressive enough. None of them are useful for programming, because usefulness is not about expressive sufficiency. It is about ease of expression.</p><p>This is why programming languages were invented. Not because transistors couldn&#8217;t express the computation, but because transistors made the computation too hard to express, reason about, modify, and communicate to other programmers. The notation mattered.</p><p>The same argument applies above the level of transistors. If a notation makes a class of problems hard to express, that is not a minor inconvenience to be overcome by sufficiently skilled programmers. It is a signal that the notation does not fit the problem domain.</p><p><strong>What hard expression costs.</strong></p><p>When a notation fights the problem, the cost is not just programmer time &#8212; though that cost is real and large. The cost is also in what does not get built.</p><p>A notation that makes concurrent systems hard to express produces fewer well-designed concurrent systems. Not because programmers are incapable, but because the overhead of expressing concurrency clearly in an ill-fitting notation consumes the budget that would have gone into design. The workaround ships. The design doesn&#8217;t.</p><p>A notation that makes state transitions hard to express produces programs where state transitions are implicit, scattered, and invisible. Not because programmers don&#8217;t understand state machines, but because the notation gives them no clean place to put one. The switch statement inside the loop ships instead.</p><p>A notation that makes multiple output channels awkward produces programs that smuggle outputs through exceptions and side channels. Not because programmers don&#8217;t know better, but because the notation makes &#8220;better&#8221; expensive.</p><p>The notation shapes the solution space. Not by preventing solutions, but by making some solutions cheap and others costly. Programmers, like water, take the path of least resistance. The notation defines which path that is.</p><p><strong>Decades of baubles.</strong></p><p>We have spent decades adding to the functional notation rather than supplementing it with alternatives. Monads for managing effects. Continuations for managing control flow. Async/await for managing the synchrony mismatch. Effect systems for managing side effects. Evaluation order rules for managing the cases where order matters.</p><p>Each addition makes the notation more capable of expressing something it was not originally designed to express. Each addition also makes the notation more complex, more demanding to learn, and further from the original idea that gave it its elegance.</p><p>The result is that the sweet spot of FP &#8212; the class of problems it expresses cleanly and cheaply &#8212; has not grown. The notation has grown around it. New programmers must now learn not just functional thinking but the accumulated stack of workarounds that make functional thinking usable outside its sweet spot. The beginner&#8217;s path into the notation runs directly through the complexity that the workarounds created.</p><p>This is not progress. This is a notation being stretched past its natural boundary because the alternative &#8212; acknowledging that different problems need different notations &#8212; has been treated as defeat.</p><p><strong>Ease of expression is an engineering requirement.</strong></p><p>In engineering, the right tool for the job is not a style preference. Using the wrong tool produces weaker results, at greater cost, with more risk of failure. A notation is a tool. A notation that does not fit the problem is the wrong tool.</p><p>The solution is not to make the wrong tool more elaborate. The solution is to have more tools and to know when to switch.</p><p>This is not a radical idea. Hardware designers use it routinely. A circuit schematic, a timing diagram, a state machine diagram, a truth table &#8212; these are different notations for different aspects of the same system. No hardware designer would insist on expressing everything in a single notation. The problem domain is too varied. Different aspects of the system have different natural notations.</p><p>Software has largely insisted on the opposite. One language, or a small family of languages sharing the same fundamental assumptions, for every problem. The notation is the craft. Questioning the notation is questioning the identity of the field.</p><p>That needs to change.</p><p><strong>What ease of expression actually requires.</strong></p><p>It requires that when we find a useful notation &#8212; function-based thinking, relational thinking, state-machine thinking, diagram-based thinking &#8212; we stop trying to make that notation do everything. We stop enbaubling it. We ask instead: what is this notation&#8217;s sweet spot, and how do we connect it to other notations for the parts of the problem it doesn&#8217;t fit?</p><p>The question is not &#8220;can this notation express the problem?&#8221; The question is &#8220;does this notation make the problem easy to express, easy to reason about, easy to modify, and easy to communicate?&#8221;</p><p>If the answer is no, the notation is the wrong tool. We are allowed to use a different one.</p><p><em>Next: UNIX Already Showed Us the Way.</em></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-2bd/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-2bd/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-2bd?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-2bd?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Case for Composable Notations (3 of 5) State Isn’t The Enemy]]></title><description><![CDATA[2026-03-30]]></description><link>https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-e6c</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-e6c</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 31 Mar 2026 02:13:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>FP didn&#8217;t eliminate state. It hid it. The field spent decades learning to avoid expressing state rather than learning to express it well. Those are opposite skills.</p><p><strong>L;R</strong></p><p>Programming courses teach state avoidance as a virtue. The word &#8220;state&#8221; has acquired a faint smell &#8212; something to be minimized, hidden, apologized for. Stateless is clean. Stateful is dangerous. Pure functions are safe. Mutation is where bugs live.</p><p>This is a notation bias dressed up as wisdom.</p><p><strong>What state actually is.</strong></p><p>State is the current condition of a system at a point in time. Every useful program has it. A program that accepts input, produces output, and changes nothing in between is not a program &#8212; it is a mathematical function. Useful for certain problems. Not a general model of computation.</p><p>CPUs mutate registers. Memory is written and rewritten. Device drivers track hardware conditions. User interfaces respond to sequences of events. Network protocols maintain connection state across time. Embedded controllers react to signals from the physical world. Every one of these domains is stateful by nature. The state does not go away because the notation ignores it.</p><p><strong>Where FP put the state.</strong></p><p>FP pushed state into the operating system. The call stack is state. The heap is state. The scheduler&#8217;s bookkeeping &#8212; which process is running, which is waiting, what it was doing when it was interrupted &#8212; is state. This is not a small amount of state. It is the complete runtime state of every active process, maintained by the OS on the programmer&#8217;s behalf.</p><p>The programmer did not make it disappear. The programmer handed it to the OS and agreed not to look at it directly. The OS shapes that state according to its own model &#8212; threads, processes, protected memory regions &#8212; and the programmer inherits that shape whether or not it fits the problem.</p><p>When it fits, the arrangement is invisible and cheap. When it doesn&#8217;t fit &#8212; when the problem has a state structure the OS model doesn&#8217;t express cleanly &#8212; the programmer pays. The payment is indirection, workarounds, and a persistent translation layer between the problem&#8217;s natural structure and the structure the runtime imposes.</p><p><strong>The vocabulary we didn&#8217;t develop.</strong></p><p>State machines are one way to name state explicitly: here are the states a system can be in, here are the conditions that cause transitions between them. The notation is direct. The structure is visible. The programmer controls it.</p><p>Harel&#8217;s Statecharts extended state machines to handle hierarchy and concurrency &#8212; evidence that the notation can be developed further. They are not the final word. They are one existence proof that expressing state clearly is possible and has been explored.</p><p>There may be better approaches. We don&#8217;t know, because the field largely stopped looking. Instead of developing richer notations for state expression, the field developed richer techniques for state avoidance. These are opposite research programmes. One asks: how do we express state clearly? The other asks: how do we avoid having to?</p><p>The avoidance programme won. State machines are taught, if at all, as a niche technique for embedded systems or compiler design &#8212; not as a general tool for expressing the stateful behaviour that most programs actually contain. Most programmers implement what are effectively state machines as switch statements inside loops, without the notation to see that is what they are doing.</p><p><strong>The cost of the avoidance programme.</strong></p><p>When state is hidden, debugging means reconstructing it. When state lives in the OS, you observe it indirectly &#8212; through stack traces, memory dumps, log files, debugger watches. The tools for reconstructing hidden state are sophisticated because the need for them is constant.</p><p>When state is expressed directly, you can see it. You can name the states your system can be in. You can describe the transitions explicitly. You can ask, at any point: what state is this system in, and why? The question is answerable without reconstruction.</p><p>Hidden state is not safe state. It is state that is harder to reason about, harder to test, and harder to modify. The sense of safety that FP&#8217;s state restrictions provide is real but narrow. It applies to the class of problems where the OS&#8217;s model of state happens to fit. For everything else, the safety is an illusion maintained at the cost of visibility.</p><p><strong>What ease of expression requires here.</strong></p><p>The transistor argument applies directly. We do not describe programs in terms of transistors because the notation is too far from the problem. We do not describe concurrent, reactive, long-lived systems in terms of pure functions for the same reason &#8212; the notation is too far from the problem. The distance between notation and problem is not a style preference. It is where bugs live, where complexity accumulates, and where programmer hours disappear.</p><p>Expressing state well means having notations in which states are named, transitions are explicit, and the current condition of the system is visible. We have glimpses of what that looks like. We have not pursued it seriously, because the dominant notation taught us that state was the enemy.</p><p>It isn&#8217;t. Invisible state is.</p><p><em>Next: Ease of Expression Is the Whole Point.</em></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-e6c/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-e6c/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-e6c?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations-e6c?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Case for Composable Notations (2 of 5) The Restrictions That Came With the Cow]]></title><description><![CDATA[2026-03-30]]></description><link>https://programmingsimplicity.substack.com/p/the-case-for-composable-notations</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/the-case-for-composable-notations</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Tue, 31 Mar 2026 02:09:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>Functional programming imposes a specific set of restrictions. Each one is a notation choice. None of them are laws of nature.</p><p><strong>L;R</strong></p><p>Every notation has a sweet spot &#8212; a class of problems it expresses clearly and cheaply. Outside that sweet spot, the notation fights you. The restrictions are not bugs; they are the shape of the cow. Understanding them precisely is the first step toward knowing when to put the cow down.</p><p>Here is what FP imposes.</p><p><strong>Single-thread thinking.</strong></p><p>FP is fundamentally sequential. What we call &#8220;multi-tasking&#8221; today is time-sharing &#8212; a technique invented to fake concurrency by splitting a single thread into separate state machines, saving their state in hidden data structures, and switching between them at boundaries the software architect does not control. The operating system manages the illusion. The programmer works inside it. Real concurrency &#8212; multiple things happening independently, with no shared thread &#8212; is not expressible in the functional notation without leaving it.</p><p><strong>One in, one out.</strong></p><p>A function takes arguments and returns a value. One bundle in, one bundle out. stderr is a workaround for the cases where you need a second output channel. Exceptions are a workaround for the cases where returning a value is the wrong response to a situation. Both are bolt-on mechanisms grafted onto a notation that does not natively support multiple outputs. The workarounds work &#8212; but they are workarounds, and their seams show.</p><p><strong>Elision of mutation.</strong></p><p>FP treats assignment as suspect. Mutation is discouraged, hidden behind monads or immutable data structures, or banned outright. This is useful for a certain class of problems. It is not a description of how computers actually work. CPUs mutate state. Memory is mutable. Pretending otherwise requires the runtime to maintain an increasingly elaborate fiction &#8212; and pushes the mutation somewhere the programmer cannot see or control it.</p><p><strong>Fixed control flow.</strong></p><p>FP offers one control flow structure: top to bottom, left to right, as inherited from the printing press. Conditionals and recursion are permitted variations within that structure. Anything else &#8212; event-driven flow, concurrent flow, flow driven by external signals &#8212; requires leaving the notation or bolting something onto it. The notation does not make other control flows easy to express. It makes them feel unnatural, because the notation itself has no vocabulary for them.</p><p><strong>Routing.</strong></p><p>In a functional program, routing is sequential by default. Data moves from caller to callee and back. Expressing any other routing topology &#8212; broadcast, fan-out, selective dispatch, feedback &#8212; requires constructing it manually from the available primitives. The notation does not help. Every non-sequential routing pattern is an exercise in working around a notation that was not designed to express it.</p><p><strong>Synchrony.</strong></p><p>Every function call is synchronous. The caller suspends. The callee runs. The caller resumes when the callee returns. This is a specific, narrow model of coordination &#8212; useful for calculator-like computation, costly for anything else. Event-driven systems, device drivers, network protocols, user interfaces, robotics &#8212; these domains are asynchronous by nature. Forcing them into a synchronous notation produces the bolt-ons: callbacks, promises, async/await, reactive frameworks. The notation keeps winning. The domain keeps losing.</p><p><strong>Evaluation order as a hidden assumption.</strong></p><p>FP inherits a specific evaluation model and mostly keeps it invisible. Some languages make it explicit &#8212; Clojure, for instance, specifies evaluation behaviour in certain contexts &#8212; but the mechanism for doing so is itself a notation-level workaround rather than a first-class tool. When you need to control the order in which things are evaluated, you should be able to say so clearly. Instead, you learn which incantations coerce the runtime into the order you need.</p><p><strong>What this adds up to.</strong></p><p>Each of these restrictions was a reasonable choice in 1960. CPUs were expensive, memory was scarce, and mathematical function application was a clean, verifiable model for a narrow class of computation. The restrictions made the cow spherical on purpose.</p><p>The problem is not the restrictions. The problem is that we have spent decades adding baubles &#8212; monads, continuations, effect systems, async layers, evaluation order rules &#8212; to stretch the notation past its sweet spot, rather than acknowledging that different problems need different notations.</p><p>We don&#8217;t still describe computers in terms of transistors. We invented higher-level notations because ease of expression matters. The same logic applies here.</p><p><em>Next: State Isn&#8217;t the Enemy.</em></p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-case-for-composable-notations?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-case-for-composable-notations?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Working on Rewriting the Preoptimizer Preopt.ohm]]></title><description><![CDATA[2026-03-29]]></description><link>https://programmingsimplicity.substack.com/p/working-on-rewriting-the-preoptimizer</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/working-on-rewriting-the-preoptimizer</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Mon, 30 Mar 2026 01:09:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>I don&#8217;t want to deal with Substack&#8217;s inferior formatting capabilities for program code, so I&#8217;ve converted this article into PDF form and am providing it as a downloadable file.</em></p><p><strong>TL;DR</strong></p><p>This article is about my ongoing work on the Frish project - namely fixing the pre-optimizer stage. At this point, I need to throw the existing code away and start again. This article contains a justification for discarding the code, and, how I make it &#8220;easy&#8221; for me to use this option. The point of designing a program is to discover the details of the problem space in an iterative manner, not to create production-ready code (yet). I discuss my thought process and the tools that I use to help me iterate and toss code away. I only discard <em>code</em>, but retain what I&#8217;ve learned. The goal is to use tools that make it easy for me to spend more time thinking and learning rather than polishing code - all the time knowing that I can throw all of the code away, since it hasn&#8217;t cost me much effort to create. This approach is different than that of using a strongly typed programming language. First, I want to figure out what I want the code to do, then I can re-cast it into stone using tools and compilers meant for Production Engineering.</p><p><strong>L;R</strong></p><p>&#8230;</p><p><strong>What I&#8217;ve learned, thus far</strong></p><p>&#8230;</p><p><strong>Refreshed Approach</strong></p><p>&#8230;</p><p><strong>Repository</strong></p><p>The WIP code is on github <a href="https://github.com/guitarvydas/frish/tree/debugdefsynonym">https://github.com/guitarvydas/frish/tree/debugdefsynonym</a>.</p><p>An accompanying twitch-like (long) <a href="https://youtu.be/05UxokdnUbI">video</a>.</p><p>I will write new code. I find it difficult to write prose about what I&#8217;m thinking or to talk into a camera while I think. If you want to follow along, see the code in the repository and/or visit the &#8220;programming simplicity&#8221; discord and poke questions at me.</p><p><strong>PDF</strong></p><div class="file-embed-wrapper" data-component-name="FileToDOM"><div class="file-embed-container-reader"><div class="file-embed-container-top"><image class="file-embed-thumbnail-default" src="https://substackcdn.com/image/fetch/$s_!0Cy0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack.com%2Fimg%2Fattachment_icon.svg"></image><div class="file-embed-details"><div class="file-embed-details-h1">Working On Rewriting The Preoptimizer Preopt</div><div class="file-embed-details-h2">180KB &#8729; PDF file</div></div><a class="file-embed-button wide" href="https://programmingsimplicity.substack.com/api/v1/file/f4902e81-ff36-4a7d-888f-f159a10e10a7.pdf"><span class="file-embed-button-text">Download</span></a></div><a class="file-embed-button narrow" href="https://programmingsimplicity.substack.com/api/v1/file/f4902e81-ff36-4a7d-888f-f159a10e10a7.pdf"><span class="file-embed-button-text">Download</span></a></div></div><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/working-on-rewriting-the-preoptimizer/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/working-on-rewriting-the-preoptimizer/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/working-on-rewriting-the-preoptimizer?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/working-on-rewriting-the-preoptimizer?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Wrong Spherical Cow - First Principles of Parts Based Programming]]></title><description><![CDATA[2026-03-18]]></description><link>https://programmingsimplicity.substack.com/p/the-wrong-spherical-cow-first-principles</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/the-wrong-spherical-cow-first-principles</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Sun, 29 Mar 2026 02:10:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong> &#8212; Function-based programming borrowed the wrong metaphor from mathematics. Erlang and Go improve on it but don&#8217;t finish the job. A simple change of point-of-view &#8212; not a rewrite &#8212; gets you the rest of the way.</p><p><strong>L;R</strong></p><p><strong>The Metaphor We Didn&#8217;t Notice We Chose</strong></p><p>Mathematical functions describe timeless relationships. When you write f(x) = x&#178; there is no time in that statement. No waiting. No concurrency. The function returns instantly, always, to exactly one caller.</p><p>This is fine for mathematics. Relations between numbers don&#8217;t happen <em>in time</em>.</p><p>When we built computers we modeled computation on mathematical functions &#8212; and silently imported the metaphysics along with the notation. CALL/RETURN encodes assumptions that feel like logic but are actually choices:</p><ul><li><p>A function always returns</p></li><li><p>It returns to its caller</p></li><li><p>It returns synchronously</p></li><li><p>Only one thing runs at a time</p></li></ul><p>These aren&#8217;t laws of nature. They&#8217;re a notation&#8217;s hidden opinions about time.</p><p><strong>The Damage</strong></p><p>CALL/RETURN couples caller and callee through a shared data structure &#8212; the stack. The caller is suspended while the callee runs. They share execution time, not just data. What looks like encapsulation is actually intimate coupling wearing a clean interface.</p><p>This is fine for computing things. It is the wrong foundation for <em>systems</em> &#8212; collections of independent components that react to events, run concurrently, and communicate across boundaries.</p><p>The real world doesn&#8217;t use CALL/RETURN. Your heart doesn&#8217;t wait for your lungs to finish. A web server shouldn&#8217;t pause all users while one request completes. Hardware interrupts don&#8217;t politely wait for your loop to yield.</p><p>Asynchrony isn&#8217;t a special case. It&#8217;s the default state of the universe. Synchrony is something you impose when you need it.</p><p><strong>Erlang and Go Got Closer</strong></p><p>Both recognised the problem. Both moved toward lightweight async components &#8212; goroutines in Go, processes in Erlang &#8212; without the heavy cost of OS-level threads and MMUs. Real progress.</p><p>But neither finished the job.</p><p>Go still allows shared memory. The async boundary is optional, not architectural. You can &#8212; and most codebases do &#8212; reach back across it.</p><p>Erlang is cleaner. Lightweight processes, message passing, no shared memory. Genuinely async by default. But an Erlang process knows its neighbours. The routing &#8212; who sends to whom &#8212; lives inside the process code as explicit PIDs and sends. The component and the wiring are fused together.</p><p>To understand an Erlang system&#8217;s architecture you have to read all the code and reconstruct the topology in your head. There is no schematic. The design is implicit.</p><p><strong>The EE&#8217;s Secret</strong></p><p>Electrical engineers build complex asynchronous systems reliably. They don&#8217;t solve Maxwell&#8217;s equations by hand for every circuit. They use a notation &#8212; the schematic &#8212; that makes asynchrony invisible as a problem because it&#8217;s already the default.</p><p>In a schematic every component reacts. Nothing waits for permission. Voltage appears, current flows, simultaneously, everywhere. There is no caller. The notation makes concurrency the background, not the foreground.</p><p>The key property: <strong>components are topology-blind</strong>. A resistor doesn&#8217;t know what it&#8217;s connected to. You can drop it into any circuit without modifying it. The schematic &#8212; not the component &#8212; encodes the design.</p><p>This is the piece Erlang and Go are missing.</p><p><strong>PBP: Change the Point of View</strong></p><p>Parts Based Programming (PBP) adds one principle to what Erlang got right:</p><p><em>Components don&#8217;t know their neighbours. Only the parent container knows the wiring.</em></p><p>Each component has named input ports and named output ports. It receives messages, does work, sends to named outputs. Who wired those outputs &#8212; and where the messages actually go &#8212; is not its concern.</p><p>This makes the schematic a first-class artifact. The architecture is explicit, separate from the implementation, readable and modifiable without touching any component.</p><p>It also means existing synchronous code doesn&#8217;t need to be rewritten. A synchronous function becomes a leaf component. The async composition layer wraps it. The resistor doesn&#8217;t change &#8212; the circuit diagram changes.</p><p>You don&#8217;t need a new language. You don&#8217;t need new hardware. You need a different place to stand.</p><p>The function-based approach isn&#8217;t wrong because it&#8217;s synchronous. It&#8217;s wrong because it made synchrony invisible as an assumption &#8212; and then made asynchrony invisible as a possibility.</p><p>PBP makes the wiring visible. That turns out to be enough.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-wrong-spherical-cow-first-principles/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-wrong-spherical-cow-first-principles/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-wrong-spherical-cow-first-principles?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-wrong-spherical-cow-first-principles?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Case for Composable Notations (1 of 5) The Spherical Cow We Forgot We Were Riding]]></title><description><![CDATA[2026-03-26]]></description><link>https://programmingsimplicity.substack.com/p/the-spherical-cow-we-forgot-we-were</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/the-spherical-cow-we-forgot-we-were</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Sun, 29 Mar 2026 01:34:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>McCarthy invented functional programming as a notation convenience &#8212; a spherical cow. The programming community forgot it was a cow. Decades of workarounds followed.</p><p><strong>L;R</strong></p><p>Physics has a tradition of inventing simplified notations to make hard problems tractable. A spherical cow is a deliberate lie &#8212; a simplification that strips away inconvenient reality so you can think clearly. The rule is: remember it&#8217;s a lie.</p><p>McCarthy invented Lisp 1.5 under the same spirit. CPUs and memory were expensive. A notation that treated computation as pure mathematical function application &#8212; no side effects, no time, no messiness &#8212; was a useful fiction. It let you reason cleanly about certain classes of problems.</p><p>The rule applies: remember it&#8217;s a fiction.</p><p>The programming community forgot.</p><p><strong>C smudged the boundary.</strong></p><p>Before C, the distinction was explicit: <em>subroutines</em> and <em>functions</em> were different things. A subroutine can have side effects. A function &#8212; by definition &#8212; cannot. A function with zero arguments that returns a value must return the <em>same</em> value every time, or it isn&#8217;t a function; it&#8217;s a subroutine wearing a function&#8217;s clothing.</p><p>After C, everything became a &#8220;function.&#8221; The word lost its meaning. Today most programmers treat a zero-argument procedure with side effects as a function. It isn&#8217;t. The confusion isn&#8217;t cosmetic &#8212; it shapes how an entire generation thinks about what programs <em>are</em>.</p><p><strong>What got lost.</strong></p><p>McCarthy&#8217;s original eval/apply engine was small and tight. That engine made sense for its era and its cost constraints. CPUs were expensive. Memory was expensive. The fiction paid for itself.</p><p>CPUs and memory are now effectively free. The original justification evaporated. What remained was the notation, now mistaken for ground truth.</p><p>The cost of maintaining the fiction has compounded. To allow &#8220;functions&#8221; to interact with the real world &#8212; which is irreducibly stateful and asynchronous &#8212; the community has had to invent a growing stack of workarounds: monads, continuations, effect systems, callback protocols, async/await layers, and more. Each workaround is a patch over the mismatch between the spherical cow and reality. Each patch makes the next patch harder to avoid.</p><p><strong>The deeper confusion: data flow vs. control flow.</strong></p><p>Functional programming is often described as &#8220;data flow.&#8221; It isn&#8217;t.</p><p>When a caller passes data to a callee in a functional call, two things happen simultaneously: data moves, and control transfers. The caller suspends. It waits. The callee must process the data <em>immediately</em> and return before the caller can proceed. That is synchronous control flow with data attached &#8212; not data flow.</p><p>Real data flow is asynchronous. You send data to a receiver. The receiver processes it in its own time. The sender does not wait. The sender does not care when processing happens. The sender does not even need to know <em>that</em> processing happened.</p><p>Functional programming cannot express this without leaving the paradigm. The workarounds (callbacks, futures, reactive streams) are all ways of bolting asynchrony onto a fundamentally synchronous notation. The bolt-on is visible everywhere &#8212; callback hell being only the most obvious symptom.</p><p><strong>The notation is not the only notation.</strong></p><p>BNF exists. It describes <em>what</em> to parse without specifying <em>how</em> to parse it. Most programmers, though, would rather write low-level parsing code in a functional style than reach for BNF, even when BNF is clearer, shorter, and more correct. The functional notation has become so dominant that alternatives feel exotic.</p><p>Prolog exists. Its relational engine is genuinely different &#8212; it lets you query a factbase in a notation suited to relational problems. Most programmers never reach for it, even when the problem is relational. Instead, they twist a functional language to approximate what Prolog does natively, at greater length and with more bugs.</p><p>Harel&#8217;s Statecharts exist. They describe stateful, reactive, hierarchical behaviour in a notation built for that purpose. Most programmers implement state machines as switch statements in a functional language instead.</p><p>The alternatives did not lose on merit. They lost because the functional notation achieved cultural dominance and crowded out the vocabulary needed to even ask whether a different notation might fit better.</p><p><strong>The actual problem.</strong></p><p>Most programmers now believe that programming <em>is</em> writing machine code for a CPU. Not designing systems. Not composing components. Not choosing a notation suited to a problem domain. Writing CPU instructions &#8212; with a functional veneer on top.</p><p>This is the wrong spherical cow. It was never meant to be the whole of programming. It was a notation for a particular class of problems, invented under particular cost constraints, in a particular era.</p><p>The constraints are gone. The notation stayed. The forgetting was the mistake.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-spherical-cow-we-forgot-we-were/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-spherical-cow-we-forgot-we-were/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/the-spherical-cow-we-forgot-we-were?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/the-spherical-cow-we-forgot-we-were?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Multiple Notations In A Single Project]]></title><description><![CDATA[2026-03-28]]></description><link>https://programmingsimplicity.substack.com/p/multiple-notations-in-a-single-project</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/multiple-notations-in-a-single-project</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Sat, 28 Mar 2026 15:46:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CsKq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I favour using many - terse - notations within a single project. One notation for each kind of issue within the project.</p><p>It&#8217;s about focusing in on a specific issue. Terseness allows you to focus better. Terseness allows you to write down what you think and to try it out. If you don&#8217;t like it or learn something new, terseness makes it thinkable to just erase what you wrote and to write something new. Too much detail, too early, discourages retrying, since you&#8217;ve already invested too much effort in the code. Making the notation terse rebalances the equation - you can spend more time thinking (and trying out) instead of spending time worrying about dotting all the i&#8217;s and crossing the t&#8217;s.</p><p>If you think that you don&#8217;t need to retry, you are engaging in Waterfall development.</p><p>A terse notation is <em>more expressive,,,</em> but only for that <em>specific</em> issue. You need a different notation when you switch to thinking about some other issue.</p><p><strong>Example</strong></p><p>I wanted to think about how to compile diagrams to code.</p><p>I could suck in diagrams in .graphML form or .svg form - so that wasn&#8217;t much of an issue.</p><p>At one point, though, I needed to fill in &#8220;semantic&#8221; information that was in the diagram but scattered about and not conveniently arranged how I would like. That is an &#8220;inferencing&#8221; problem.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CsKq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CsKq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 424w, https://substackcdn.com/image/fetch/$s_!CsKq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 848w, https://substackcdn.com/image/fetch/$s_!CsKq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 1272w, https://substackcdn.com/image/fetch/$s_!CsKq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CsKq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png" width="951" height="323" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:323,&quot;width&quot;:951,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:100584,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/192425030?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CsKq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 424w, https://substackcdn.com/image/fetch/$s_!CsKq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 848w, https://substackcdn.com/image/fetch/$s_!CsKq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 1272w, https://substackcdn.com/image/fetch/$s_!CsKq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F130c6639-cfe0-4330-8dd1-dc488faa9649_951x323.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Prolog notation is much, much better for dealing with inferencing than anything else I&#8217;ve found, e.g. loops within loops, recursive functions, etc. So, I expressed the pattern matching relationships in a way that made sense to me. I let a Prolog engine chew through the relationships and produce the semantic info in a way that I wanted.</p><p>For example, &#8216;this pill-shaped figure is a &#8220;port&#8221; that belongs to that rectangular figure (a &#8220;part&#8221;). And, this port is an <em>output</em> port because the tail end of an arrow is attached to it. Repeat for every pill-shaped figure and create facts that relate each port to each part and facts that say in a convenient way which direction each port is (in, out).</p><p>Later, though, I just wanted to write this stuff out into a text file (fyi: I&#8217;m using JSON, because it&#8217;s easy and ubiquitous) and fool around more with the text. Prolog makes it difficult to deal with strings. JavaScript (!) is easier for this kind of thing.</p><p>For me, Prolog was the syntax of choice for the inferencing stuff, but JS (Python, etc. - (fyi: in fact, I&#8217;m using OhmJS and my T2T tools)) was the syntax of choice for the rest of the text-manipulation stuff. If I had to choose only one language for this, I would have had to struggle with either Prolog or with JS. I wanted to use both (this is but a simple example, I actually use lots of languages).</p><p>In my view, a GPL - General Purpose Language - like Rust, Python, Haskell, C++ can&#8217;t express everything I need to think about without causing me extra work. You can see GPL design heading towards either milquetoast unions of a variety of features, or, too much emphasis on only one kind of feature (e.g. type checking, user-defined structs, etc.).</p><p>GPLs are good for optimizing and for creating production-ready code, but not so good for thinking about problems and their solutions. The way I see it, creating a program consists of (at least) two major activities:</p><ol><li><p>Thinking about the problem and verifying that my intended solution works well enough (Design Engineering)</p></li><li><p>Creating a production-ready version of the final, chosen design. Using automated tools to help me find loopholes (Production Engineering)</p></li></ol><p>(I&#8217;m consciously skipping over other issues, like release, maintenance, security, etc.).</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/multiple-notations-in-a-single-project/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/multiple-notations-in-a-single-project/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/multiple-notations-in-a-single-project?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/multiple-notations-in-a-single-project?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Drafting Rules for Node-and-Wire Diagrams]]></title><description><![CDATA[2026-03-26]]></description><link>https://programmingsimplicity.substack.com/p/drafting-rules-for-node-and-wire</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/drafting-rules-for-node-and-wire</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Thu, 26 Mar 2026 15:44:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3c325a73-c8b9-4df5-a9de-4d06bd6781a2_441x272.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>Several rules for readable node-and-wire DPL diagrams. The underlying constraint: diagrams earn their place only where text fails.</p><p><strong>L;R</strong></p><p><strong>DPL</strong></p><p>DPL stands for Diagrammatic Programming Language.</p><p><strong>Use diagrams where text fails, and vice versa</strong></p><p>Diagrams and text are complementary, not competing. Use diagrams for topology &#8212; what connects to what. Use text for logic and annotation. Neither is universal; both are necessary.</p><p>It is worth noting that SVG, for instance, treats rectangular figures and figures containing text as peers at the same structural level &#8212; the underlying representation does not privilege one over the other, which is exactly the right default.</p><p><strong>No diagram should look complicated at first glance</strong></p><p>Complexity hides in layers. Each layer must be independently legible without forcing the reader to confront detail they haven&#8217;t asked for &#8212; while still providing that detail elsewhere, so that the complete system loses nothing overall. The reader pulls detail on demand; the diagram does not push it. This requires async, message-passing architecture: synchronous/sequential languages cannot produce genuinely independent layers without incurring extra work. Implicit layering in a synchronous system is a polite fiction.</p><p><strong>Components may have multiple input ports and multiple output ports</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Exbg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Exbg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 424w, https://substackcdn.com/image/fetch/$s_!Exbg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 848w, https://substackcdn.com/image/fetch/$s_!Exbg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 1272w, https://substackcdn.com/image/fetch/$s_!Exbg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Exbg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic" width="429" height="262" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:262,&quot;width&quot;:429,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5049,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/192218755?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Exbg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 424w, https://substackcdn.com/image/fetch/$s_!Exbg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 848w, https://substackcdn.com/image/fetch/$s_!Exbg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 1272w, https://substackcdn.com/image/fetch/$s_!Exbg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47e8ba94-5725-4964-a5f2-5deb4828ed2b_429x262.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A node is not limited to one wire in and one wire out. Ports are named and positioned on the edges of a box. Multiple inputs arrive independently; multiple outputs depart independently.</p><p><strong>Fan-out</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p3H_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p3H_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 424w, https://substackcdn.com/image/fetch/$s_!p3H_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 848w, https://substackcdn.com/image/fetch/$s_!p3H_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 1272w, https://substackcdn.com/image/fetch/$s_!p3H_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p3H_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic" width="441" height="272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:272,&quot;width&quot;:441,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4279,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/192218755?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!p3H_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 424w, https://substackcdn.com/image/fetch/$s_!p3H_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 848w, https://substackcdn.com/image/fetch/$s_!p3H_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 1272w, https://substackcdn.com/image/fetch/$s_!p3H_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4665f2c-c23f-43c4-89c4-cc914ccf6138_441x272.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fan-out &#8212; one output port wired to multiple downstream components &#8212; is natural in a node-and-wire diagram. The functional programming paradigm discourages it, because in software, fan-out requires copying values and subsequent garbage collection. In a message-passing system, the cost is explicit and the copies are intentional.</p><p><strong>Fan-in</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i9k-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i9k-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 424w, https://substackcdn.com/image/fetch/$s_!i9k-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 848w, https://substackcdn.com/image/fetch/$s_!i9k-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 1272w, https://substackcdn.com/image/fetch/$s_!i9k-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i9k-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic" width="441" height="272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:272,&quot;width&quot;:441,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3859,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/192218755?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i9k-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 424w, https://substackcdn.com/image/fetch/$s_!i9k-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 848w, https://substackcdn.com/image/fetch/$s_!i9k-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 1272w, https://substackcdn.com/image/fetch/$s_!i9k-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2812099-521b-4a8a-b2a1-86cc5df46c4c_441x272.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Fan-in &#8212; multiple upstream components wired to one input port &#8212; looks the same in both paradigms but behaves very differently. In a message-passing system, each arriving message drops into the receiver&#8217;s input queue in order of arrival; the receiver processes them in turn, and senders do not wait. In a function-call system, multiple callers sharing a single callee only <em>look</em> like fan-in: it is actually serialized, synchronous access &#8212; each caller blocks until the callee finishes before the next caller can proceed.</p><p><strong>Feedback is a first-class citizen</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qaOv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qaOv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 424w, https://substackcdn.com/image/fetch/$s_!qaOv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 848w, https://substackcdn.com/image/fetch/$s_!qaOv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 1272w, https://substackcdn.com/image/fetch/$s_!qaOv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qaOv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic" width="460" height="176" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:176,&quot;width&quot;:460,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3321,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://programmingsimplicity.substack.com/i/192218755?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qaOv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 424w, https://substackcdn.com/image/fetch/$s_!qaOv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 848w, https://substackcdn.com/image/fetch/$s_!qaOv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 1272w, https://substackcdn.com/image/fetch/$s_!qaOv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F109bbe3c-ba97-41a8-a5a4-1b045ffd94d0_460x176.heic 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A wire that loops back &#8212; from a downstream component to an upstream one &#8212; is not a special case. It is a normal connection, drawn with the same right-angle routing as any other wire. Feedback is how systems remember, regulate, and adapt.</p><p>Feedback is not the same as recursion, and the difference matters. Imagine a line of people waiting at a ticket window. Feedback sends a person to the <em>back</em> of the line &#8212; they wait their turn. Recursion sends a person to the <em>front</em>, cutting in ahead of everyone else. In software terms: feedback drops a message into a queue and returns immediately; recursion suspends the current call and demands an answer before continuing. One is async and non-blocking; the other is sync and stack-consuming.</p><p><strong>Let the compiler determine meaning from context &#8212; minimize symbol decoration</strong></p><p>The IEC eventually replaced the zigzag resistor symbol with a plain rectangle. The zigzag was a pictogram of the physical component&#8217;s wound-wire construction &#8212; a piece of historical baggage baked into the notation. A labeled rectangle is cleaner, and the label carries all the semantic weight needed.</p><p>The same principle applies to software diagrams. In most popular programming languages, an identifier carries no visible decoration indicating whether it names a variable, a parameter, a function, a type, or something else &#8212; the compiler resolves that from context. A node-and-wire diagram can work the same way: use the smallest possible set of shapes, and let position, port names, and wiring topology convey meaning. Every additional symbol variant &#8212; every special shape that encodes a semantic distinction &#8212; is a vocabulary item the reader must memorize. Prefer one symbol with annotations over two symbols with implicit meanings.</p><p><strong>Lines do not cross box boundaries</strong></p><p>A line crossing a box boundary is two lines with a hidden junction at the edge. Make that explicit: ports are visible marks on a box&#8217;s edges. Crossing a boundary without a named port silently destroys layer independence.</p><p><strong>Right-angle lines only; no crossings</strong></p><p>Rounded right-angle bends. No crossing lines. No free-form curves. The diagram compiler only needs to know which port connects to which port &#8212; it does not care where on a box&#8217;s edge a port sits. So: move ports freely along edges to eliminate crossings. The result is visually cleaner, and the semantics are identical.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/drafting-rules-for-node-and-wire/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/drafting-rules-for-node-and-wire/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/drafting-rules-for-node-and-wire?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/drafting-rules-for-node-and-wire?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Architectural Portability]]></title><description><![CDATA[2026-03-11]]></description><link>https://programmingsimplicity.substack.com/p/architectural-portability</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/architectural-portability</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Sat, 21 Mar 2026 19:52:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TL;DR</strong></p><p>The most re-usable architecture is the smallest one.</p><p>Write a problem-specific notation that emits code in several languages. It doesn&#8217;t need to be Turing complete. It just needs to capture the problem structure. You now own the semantic level above any particular target language.</p><p>Going from tight to loose is easy. The other direction is the disassembler problem &#8212; semantic information has been dropped and you have to re-infer it. Keep the high-level description alive. Emit from it.</p><p>Working code is working code, whether written by a human, a generator, or an LLM. Test it either way. When you take code from a repo, freeze it &#8212; copy the source, cut the live dependency. Start from repos. Don&#8217;t end there.</p><p>You can get through alpha several ways: type-checked languages, LLM-generated code, or a REPL-based dynamic language that keeps the program alive while you&#8217;re writing it. Programming languages are stone-age IDEs. The REPL is a fundamentally different relationship with the running system. Use it.</p><p>Compilation is an optimization, not a methodology. It belongs after beta. And compiled code needs to be tested again before production &#8212; it&#8217;s a different artifact from what you tested. Develop dynamically &#8594; beta test &#8594; compile &#8594; gamma test &#8594; ship.</p><p>Type safety is not the finish line. Shipping software that does what users need is.</p><p><strong>L;R</strong></p><p>The most re-usable architecture is the smallest one.</p><p>This sounds obvious until you watch someone spend six months building a framework that can do everything, and then spend the next six months explaining why it can&#8217;t quite do <em>this particular thing</em>.</p><p>Small and simple isn&#8217;t a concession. It&#8217;s the goal. A small architecture fits in your head. It composes. It gets out of the way. And critically &#8212; you can pick it up and drop it into a new context without dragging half your previous project along for the ride.</p><p><strong>The Meta-Language Move</strong></p><p>Here&#8217;s a technique that pays off disproportionately: instead of writing your solution directly in one programming language, write a problem-specific notation that <em>emits</em> code in several languages.</p><p>A word on the &#8220;new language is confusing&#8221; objection. If you write your logic in a standard GPL &#8212; Python, Rust, whatever &#8212; a maintainer (possibly yourself, six months from now) still has to decipher what you intended. That work doesn&#8217;t disappear just because the language is familiar. And if you write it in a language from Mars, they still have to learn the syntax. But syntax is the easy part. The hard part is always the same: understanding the architecture, the DI &#8212; Design Intent.</p><p>Current GPLs are not much better than assembler in this regard. They specify the details of operation in ways so excruciatingly detailed that only a machine could love them. That&#8217;s the opposite of what a high-level language is supposed to do. The point of abstraction is to summarize <em>what</em> &#8212; the DI &#8212; not to drown you in <em>how</em>.</p><p>If the language from Mars describes your DI in human terms better than the equivalent GPL code, the maintainer pays a small one-time cost to learn the Martian syntax, then spends less time total figuring out what the software is actually doing. The <em>how</em> &#8212; the excruciating details &#8212; should remain available. But it shouldn&#8217;t be the only way in, or the first way in.</p><p>Your notation doesn&#8217;t need to be Turing complete. It doesn&#8217;t need a type system, a module system, or a five-year standardization process. It just needs to be good enough to capture the problem structure and emit reasonable code in Python, JavaScript, whatever you need this week.</p><p>This is not a new idea &#8212; it&#8217;s the idea behind every successful DSL, every template engine, every macro system worth using. What&#8217;s new is that the tooling to build these things has gotten embarrassingly cheap. OhmJS, PEG parsers, even a few hundred lines of Python with regex &#8212; you can have a working transpiler in an afternoon.</p><p>The payoff: you now own the semantic level above the target language. Want to emit Rust next year instead of C? The translation lives in one place. Your problem description hasn&#8217;t changed.</p><p><strong>Tighter to Looser, Not the Other Way</strong></p><p>There&#8217;s an asymmetry here that&#8217;s worth naming. Going from a tighter, more constrained language to a looser one is easy. Going the other direction is hard &#8212; it&#8217;s the disassembler problem.</p><p>When you compile down, semantic information evaporates. Variable names become offsets. Intent becomes instruction sequences. Structure becomes flat. And if you later need to go back <em>up</em> &#8212; to recover that intent, to work at the problem level again &#8212; you have to re-infer what was thrown away. That inference is never complete. You&#8217;re always guessing.</p><p>This is why architecture matters at the source level. Don&#8217;t throw away the structure prematurely. Keep the high-level description alive. Emit from it, don&#8217;t abandon it.</p><p><strong>Should You Let an LLM Write the Code?</strong></p><p>Sure. Why not?</p><p>Working code is working code. It doesn&#8217;t matter whether a human typed it, a generator emitted it, or an LLM hallucinated it into existence on the third attempt. What matters is whether it passes the tests.</p><p>The anxiety about LLM-generated code usually confuses <em>authorship</em> with <em>correctness</em>. These are orthogonal. You&#8217;ve always needed to test code written by other humans. You test code written by yourself. Testing LLM output is the same discipline, applied to a new source.</p><p>One practical note: when you take code from a repo &#8212; whether written by a human or generated &#8212; <em>freeze it</em>. Copy the source. Don&#8217;t maintain a live dependency on something you don&#8217;t control. Debug it until it works, then protect it from future upstream changes. The goal is a product that works, not a product that works <em>today</em> but silently breaks when some package maintainer makes a different choice next month.</p><p>Start from repos. Don&#8217;t end there.</p><p><strong>What LLMs Actually Reveal About Type Checking</strong></p><p>LLMs have made something visible that was always true but easy to ignore: you can produce working software without type-checking your way to apparent correctness.</p><p>This doesn&#8217;t mean type checking is useless. It means type checking is <em>one tool</em>, not the destination.</p><p>The actual goal is getting to beta testing as fast as possible. Beta is where real feedback lives. Alpha is where you&#8217;re still arguing with yourself about the shape of the problem.</p><p>There are several reasonable paths through alpha.</p><p>You can use a tight, strongly-typed language and let the type checker catch structural mistakes early. Or you can use LLM-generated code and move fast on the assumption that tests will catch mistakes instead. Or &#8212; and this one gets underrated &#8212; you can use a REPL-based dynamic language and develop interactively, tightening the loop between idea and running code to almost nothing. This is what &#8220;RAD&#8221; actually means when it&#8217;s working.</p><p>Programming languages, when you squint at them, are stone-age IDEs. They were designed in an era when you submitted a deck of cards and came back the next morning. Dynamic languages broke from that model: the program is alive while you&#8217;re writing it. You poke it. It answers. You adjust. The REPL isn&#8217;t a toy feature &#8212; it&#8217;s a fundamentally different relationship between the programmer and the running system.</p><p>AOT-compiled languages trade that interactivity away. Which is fine, eventually. But compilation is an <em>optimization</em>, not a development methodology. It belongs at the end of the process, not woven into every iteration. You compile after beta. Not before.</p><p>Compiled code also needs to be tested <em>again</em> before it goes to production. The compilation step is a transformation. Transformations can introduce problems &#8212; optimizer bugs, platform differences, linking surprises. If beta was your dynamic version, you haven&#8217;t actually tested the compiled artifact. Call it gamma testing if you want a name for it. The point is: the compiled binary is a different thing from the program you tested, and treating it as automatically identical is wishful thinking.</p><p>The sequence should be: develop dynamically &#8594; beta test &#8594; compile &#8594; gamma test &#8594; ship. Not: refuse to run the program until the types check out.</p><p>What&#8217;s not reasonable is treating type safety as the end-state &#8212; the proof that the software is done. Software is done when it does what users need it to do. That&#8217;s a test. Go run it.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/architectural-portability/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/architectural-portability/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/architectural-portability?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/architectural-portability?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Who Am I and Why Should You Care?]]></title><description><![CDATA[2026-03-21]]></description><link>https://programmingsimplicity.substack.com/p/who-am-i-and-why-should-you-care</link><guid isPermaLink="false">https://programmingsimplicity.substack.com/p/who-am-i-and-why-should-you-care</guid><dc:creator><![CDATA[Paul Tarvydas]]></dc:creator><pubDate>Sat, 21 Mar 2026 19:07:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eSyZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d69eba9-d5d3-4387-a046-9b881b00bd45_131x131.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>A quick note on some changes here: I&#8217;m keeping all content free, but I&#8217;m turning on voluntary paid subscriptions. If the work is useful to you, a paid subscription is appreciated but never required.</em></p><p><em>I&#8217;m also still learning Substack&#8217;s features, so you may see incremental changes in how posts are delivered over time &#8212; the goal is to reduce email noise without restricting access to anything. Suggestions are welcome.</em></p><p><strong>Who Am I and Why Should You Care?</strong></p><p>I&#8217;m a retired electrical engineer who ran a software consultancy for over 25 years. Embedded systems (credit card terminals, the kind of hardware that has to work every single time), Y2K renovation of COBOL codebases using advanced parsing and backtracking tools and design recovery, tax platforms, business strategy tools &#8212; I&#8217;ve built the unglamorous stuff that actually runs the world. None of it was glamorous. All of it taught me something.</p><p>What it taught me, mostly, is that software is far more complicated than it needs to be.</p><p>Hardware designers work with schematics and ICs &#8212; reliable, composable, and honest about time. Software designers inherited notation from the 15th century printing press, then the 19th century QWERTY typewriter, and never stopped to ask whether those tools were the right fit for a fundamentally different medium.</p><p>They aren&#8217;t.</p><p>There&#8217;s a trap worth naming early: casting deep thinking into mathematical form is a one-way operation, not two-way. Once you understand something, you can express it in math &#8212; but the math doesn&#8217;t contain everything you understood. Whatever was elided to make the expression concise is gone. The map is not the territory, and the equation is not the insight. I try to keep that distinction front of mind in everything I write here.</p><p><strong>The core thesis</strong></p><p>Real life is asynchronous. Software pretends it isn&#8217;t. That pretense is the source of most of the accidental complexity we spend our careers managing &#8212; callback hell, promises, mutex locks, race conditions, the whole zoo.</p><p>I&#8217;ve spent years asking one question: why are hardware designs more robust than software designs? Hardware is fundamentally asynchronous &#8212; components react to signals, they don&#8217;t wait their turn. If we expressed software the same way, would reliability go up? I think yes. And I don&#8217;t think the current answers &#8212; threads, promises, async/await &#8212; are the right path. Bolting asynchrony onto a synchronous foundation feels fundamentally backwards. What we&#8217;re doing today is like writing assembler. We need better notations for expressing compositions of asynchronous components. Can we just start from asynchrony instead? I&#8217;m now convinced the answer is yes, and that doing so leads to something almost embarrassingly simple. If you know how to build a queue, you&#8217;re most of the way there.</p><p><strong>What I publish</strong></p><p>I&#8217;ve put out hundreds of articles on GitHub Pages and Substack, and hundreds of videos on YouTube. Most of it is free. I believe in partial marks &#8212; publish the unpolished thought, not just the finished paper. A lot of what I write reads more like a working diary than a journal article, and I think that&#8217;s a feature, not a bug.</p><p>The topics look scattered from the outside. Diagrams as code. Prolog. Forth. Functional programming&#8217;s hidden assumptions.</p><p>They&#8217;re connected. The connecting thread is always the same question: what is being elided, and what does that cost us?</p><p><strong>What I&#8217;m driving at</strong></p><p>We are still using 2D notation &#8212; text, math symbols, sequential syntax &#8212; to describe programs that run on hardware that lets us think in four dimensions. x, y, z, and time. We have the technology to express programs in 4D. We just haven&#8217;t built the habit.</p><p>Functional programming elides time and ordering. That&#8217;s fine for ballistics computations. It is not fine for systems that need to respond to the world as it actually is &#8212; concurrent, event-driven, asynchronous by nature.</p><p>I&#8217;m not arguing that functions are wrong. I&#8217;m arguing that functions are not enough, and that treating them as the only legitimate notation is what got us into this mess.</p><p>The path out involves asynchronous-by-default design, diagram-based notations, staged computation, and the willingness to admit that no single notation covers all bases. Software is catching up slowly.</p><p>I&#8217;m trying to help it catch up faster.</p><p><strong>See Also</strong></p><p><em>Email</em>: <a href="mailto:ptcomputingsimplicity@gmail.com">ptcomputingsimplicity@gmail.com</a></p><p><em>Substack</em>: <a href="http://paultarvydas.substack.com">paultarvydas.substack.com</a></p><p><em>Videos</em>: <a href="https://www.youtube.com/@programmingsimplicity2980">https://www.youtube.com/@programmingsimplicity2980</a></p><p><em>Discord</em>: <a href="https://discord.gg/65YZUh6Jpq">https://discord.gg/65YZUh6Jpq</a></p><p><em>Leanpub</em>: [WIP] <a href="https://leanpub.com/u/paul-tarvydas">https://leanpub.com/u/paul-tarvydas</a></p><p><em>Twitter</em>: @paul_tarvydas</p><p><em>Bluesky:</em> @paultarvydas.bsky.social</p><p><em>Mastodon: </em>@paultarvydas</p><p><em>(earlier)</em> <em>Blog:</em> <a href="http://guitarvydas.github.io">guitarvydas.github.io</a></p><p><em>References:</em> <a href="https://guitarvydas.github.io/2024/01/06/References.html">https://guitarvydas.github.io/2024/01/06/References.html</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/who-am-i-and-why-should-you-care/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/who-am-i-and-why-should-you-care/comments"><span>Leave a comment</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/p/who-am-i-and-why-should-you-care?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/p/who-am-i-and-why-should-you-care?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://programmingsimplicity.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://programmingsimplicity.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item></channel></rss>