In the 1970s, Lisp and APL REPLs represented something we’ve lost: true interactive, exploratory programming environments where developer feedback was instantaneous, context was persistent, and the entire system was designed around the developer’s cognitive workflow. Today’s programming environments—scattered across language-specific interpreters, IDEs, and build pipelines—fragment this experience. We optimized for production efficiency at the cost of development experience. It’s time to ask: what would it look like to reclaim what early REPLs did right, while leveraging modern technology to make it universally available?
REPLs
REPLs prioritize developer productivity and feedback speed (fast iteration, quick testing, interactive debugging) over production-level performance (low memory usage, fast execution, minimal startup time).
In a REPL, you want:
Instant feedback: Type an expression, see the result immediately
Quick iteration: Modify a function, re-test it, all in the same session
Minimal context-switching: Stay in the language environment rather than repeatedly invoking the compiler/interpreter
This is fundamentally different from production code, where you want:
The Trade-Off
A REPL might maintain large in-memory symbol tables, keep debug information readily available, and avoid aggressive optimizations that would slow down recompilation—all to serve the interactive developer. A production system would strip these away[1][3][2].
Is the Command Line a REPL?
The UNIX command line does resemble a REPL in structure, but it lacks critical features to be a true REPL.
How the UNIX Shell Resembles a REPL
The UNIX command line does follow a read-eval-print loop:
Read: It reads commands you type
Eval: It executes those commands (usually by spawning programs)
Print: It displays output
Loop: It waits for the next command
What’s Missing: Some Critical REPPL Features
To make the UNIX shell a proper REPL (in the sense of Lisp or APL), you would need, at least:
Persistent Language State
A REPL maintains state across expressions.
In the 1970s, keeping state in memory was a practical necessity due to slow disk I/O. However, SSDs and modern storage have fundamentally changed this calculation. A REPL designed today could reasonably use file-based persistence as its primary mechanism, trading some microseconds of latency for better durability, shareability, and integration with development tools.
For decades, programming languages have tried to optimize for both development and production simultaneously, a conflation that created unnecessary complexity. Modern technology—particularly fast storage and dynamic compilation—makes it practical and desirable to separate these concerns entirely. A development environment optimized purely for turn-around time, iteration, and exploration, paired with a deployment pipeline optimized for efficiency, would provide a superior workflow to attempting to optimize for both simultaneously.
While it may seem inefficient, using JSON as the serialization format for persistent state provides universal interoperability across languages and tools, making it far superior to binary formats for a development-focused workflow where inspection, version control integration, and cross-platform compatibility matter more than raw storage efficiency.
One might object that JSON is inefficient for persistent state compared to binary formats. But this objection conflates development concerns with production concerns. During development, debuggability and universality matter far more than storage efficiency. JSON’s human-readable format, cross-language compatibility, and seamless integration with version control systems and inspection tools provide genuine value during development—value that would be lost in a more ‘efficient’ binary format. The apparent inefficiency is actually an appropriate optimization for the development use case.
Completing the REPPL Loop - Persistence in Addition to Print
This five-step cycle—Read, Evaluate, Print, Persist, and Loop—we term a REPPL (Read-Eval-Print-Persist-Loop). While classical language REPLs persist state implicitly through in-memory runtime management, a REPPL makes persistence explicit and architectural: results must be serialized to JSON and written to persistent storage so they’re accessible to subsequent commands. A REPPL recognizes that persistence is not an afterthought or implementation detail, but a first-class requirement of interactive development. Not all commands require persistence—side-effect-only operations can display output without writing to storage—but state-changing operations must serialize their results. By making the persist step explicit, a REPPL-based OS can support continuous, inspectable, versionable context across sessions and developers.
The Print step displays results to the user, but ‘display’ is flexible and implementation-agnostic. Output need not be confined to console text; results could be streamed as JSON items to browser windows, graphical interfaces, remote displays, or specialized visualization tools. This decoupling of the evaluation engine from the presentation layer allows REPPL systems to support diverse interfaces—from terminal-based to web-based to IDE-integrated—while maintaining a unified, persistent, JSON-based backend. A single REPPL engine could serve multiple clients with different display capabilities, all sharing the same persistent state.
Future
At the deepest level, computers execute only one programming language: machine code. Every other ‘programming language’ is fundamentally a helper tool for human developers—a means of abstraction and expression that ultimately compiles down to machine instructions. This observation suggests we’ve been defining ‘programming language’ too narrowly. A complete definition must encompass not just syntax and semantics for expressing computation, but the entire ecosystem that enables development: pipelines of isolated software units, message feedback loops, asynchronous message passing, and interactive development environments. By this broader definition, operating systems are not some special category apart from programming languages—they are themselves programming languages and programming environments.
This reframing opens a radical possibility: rather than embedding REPLs within individual programming languages, we can recognize that the operating system already provides the essential infrastructure for a universal development environment. The vision is to build REPPL engines—interactive five-step environments (Read, Evaluate, Print, Persist, and Loop) that make persistence explicit and architectural—at the OS level. Using file-based JSON state as a universal serialization layer, leveraging modern fast storage, and treating the shell as an evaluation engine, we could create development workflows that combine the interactivity and exploration that made 1970s Lisp and APL REPLs powerful with the universality and tool integration that modern polyglot development requires.
Realizing this vision requires rethinking fundamental concepts: How can REPPL semantics be abstracted beyond language-specific implementations? Can a file-based, JSON-persistent development environment provide the same iteration speed and contextual continuity as traditional in-memory REPLs? What does it mean to recognize the operating system as a programming language, and how would development practice change if we fully embraced this perspective? By pursuing these questions, we may discover that the most powerful development tools aren’t those that embed REPLs within languages, but those that build REPPL engines at the OS level—making interactive, exploratory development the default mode for all programming, across all languages and implementations, mediated through the programming language we’ve had all along: the operating system itself.
Further Reading
On repl-driven programming - by mikel evins https://mikelevins.github.io/posts/2020-12-18-repl-driven/
Lather, Rinse, Repeat: A Tour of the REPL https://gigamonkeys.com/book/lather-rinse-repeat-a-tour-of-the-repl
See Also
Email: [email protected]
Substack: paultarvydas.substack.com
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/65YZUh6Jpq
Leanpub: [WIP] https://leanpub.com/u/paul-tarvydas
Twitter: @paul_tarvydas
Bluesky: @paultarvydas.bsky.social
Mastodon: @paultarvydas
(earlier) Blog: guitarvydas.github.io
References: https://guitarvydas.github.io/2024/01/06/References.html