我们失去了一些东西:20世纪70年代的REPL比现代开发环境更好。
We Lost Something: 1970s REPLs Were Better Than Modern Development Environments

原始链接: https://programmingsimplicity.substack.com/p/we-lost-something-1970s-repls-were

## 重拾交互式开发:REPPL 愿景 现代编程环境牺牲了早期 REPL(读取-求值-打印循环)的即时反馈和持久上下文,例如 1970 年代 Lisp 和 APL 中的 REPL,优先考虑生产效率而非开发者体验。这导致了解释器、IDE 和构建管道之间碎片化的工作流程。 核心思想是利用当今的技术重建这种失去的交互性。真正的 REPL 优先考虑快速迭代和调试,而不是原始性能,保持状态并提供即时结果。虽然 UNIX shell *类似于* REPL,但它缺乏持久语言状态等关键特性。 作者提出了一种 **REPPL**(读取-求值-打印-**持久化**-循环)——一个环境,其中每个改变状态的操作都会显式保存其结果,理想情况下使用通用兼容的 JSON 格式。这与以牺牲可调试性为代价来优化速度形成对比。快速存储使持久化可行,提供持久性和可共享性。 最终,该愿景超越了特定于语言的 REPL,扩展到操作系统本身,将其重新设想为通用的开发环境。通过在操作系统级别构建 REPPL 引擎,我们可以在所有语言中实现真正交互式、探索性开发,利用操作系统作为基本的“编程语言”。

一场 Hacker News 的讨论围绕着一个观点:20 世纪 70 年代的 REPL(读取-求值-打印循环)提供了比现代 IDE 更好的开发体验。用户认为,像早期 Lisp 和 Smalltalk 这样的环境中,直接与代码交互的简单性,在如今复杂的工具中常常丢失。 一些评论者指出,这些经典的 REPL 体验*仍然存在*,并提到了 iPython、带有 Calva VS Code 扩展的 Clojure,甚至浏览器开发者工具作为现代的替代品。将代码视为数据的能力——Lisp 和 Pharo 的优势——能够实现强大的自动化推理和操作。 最终,这场对话突显了对简单性和直接代码交互的渴望,许多人发现,一个基本的终端、文本编辑器(如 Vim)和一个 REPL 提供了一种出奇有效且令人耳目一新的工作流程,避免了某些现代 IDE 的臃肿和不稳定。
相关文章

原文

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:

  1. Read: It reads commands you type

  2. Eval: It executes those commands (usually by spawning programs)

  3. Print: It displays output

  4. 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

Leave a comment

Share

联系我们 contact @ memedata.com